Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 25.4.4

- Improved user property recording order with respect to sessions and events.

## 25.4.3

- Added filtering capability to `content` interface through `enterContentZone(contentFilterCallback)`.
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/bridged_utils.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function initMain(name, version) {
}

const SDK_NAME = "javascript_native_web";
const SDK_VERSION = "25.4.3";
const SDK_VERSION = "25.4.4";

// tests
describe("Bridged SDK Utilities Tests", () => {
Expand Down
139 changes: 139 additions & 0 deletions cypress/e2e/up_order.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* eslint-disable cypress/no-unnecessary-waiting */
/* eslint-disable require-jsdoc */
var Countly = require("../../lib/countly");
var hp = require("../support/helper");

Countly.q = Countly.q || [];

function initMain() {
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://your.domain.count.ly",
test_mode_eq: true,
test_mode: true
});
}

const userDetailObj = hp.userDetailObj;

// an event object to use
const eventObj = {
key: "in_app_purchase",
count: 3,
sum: 2.97,
dur: 300,
segmentation: {
app_version: "1.0",
country: "Tahiti"
}
};
const custUP = {
custom: { "name": "John Doe"}
}

describe("User properties order test", () => {
it("User details order test", () => {
hp.haltAndClearStorage(() => {
initMain();
Countly.begin_session();
Countly.add_event(eventObj);
Countly.user_details(userDetailObj);
Countly.add_event(eventObj);
Countly.add_event(eventObj);
Countly.user_details(userDetailObj);
cy.fetch_local_event_queue().then((eq) => {
expect(eq.length).to.equal(0);
});
cy.wait(500).then(() => {
cy.fetch_local_request_queue(hp.appKey).then((rq) => {
expect(rq.length).to.equal(5);
cy.log(rq);
cy.check_session(rq[0]);
cy.check_event(JSON.parse(rq[1].events)[1], eventObj, undefined, false);
cy.check_user_details(rq[2], userDetailObj);
cy.check_event(JSON.parse(rq[3].events)[0], eventObj, undefined, false);
cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false);
cy.check_user_details(rq[4], userDetailObj);
});
});
});
});
it("User details order test, async", () => {
hp.haltAndClearStorage(() => {
initMain();
Countly.q.push(['track_sessions']);
Countly.q.push(['add_event', eventObj]);
Countly.q.push(['user_details', userDetailObj]);
Countly.q.push(['add_event', eventObj]);
Countly.q.push(['add_event', eventObj]);
Countly.q.push(['user_details', userDetailObj]);
cy.fetch_local_event_queue().then((eq) => {
expect(eq.length).to.equal(0);
});
cy.wait(500).then(() => {
cy.fetch_local_request_queue(hp.appKey).then((rq) => {
expect(rq.length).to.equal(5);
cy.log(rq);
cy.check_session(rq[0]);
cy.check_event(JSON.parse(rq[1].events)[1], eventObj, undefined, false);
cy.check_user_details(rq[2], userDetailObj);
cy.check_event(JSON.parse(rq[3].events)[0], eventObj, undefined, false);
cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false);
cy.check_user_details(rq[4], userDetailObj);
});
});
});
});
it("User data order test", () => {
hp.haltAndClearStorage(() => {
initMain();
Countly.userData.set("name", "John Doe");
Countly.begin_session();
Countly.userData.set("name", "John Doe");
Countly.add_event(eventObj);
Countly.userData.set("name", "John Doe");
Countly.user_details(userDetailObj);
cy.fetch_local_event_queue().then((eq) => {
expect(eq.length).to.equal(0);
});
cy.wait(500).then(() => {
cy.fetch_local_request_queue(hp.appKey).then((rq) => {
expect(rq.length).to.equal(6);
cy.log(rq);
cy.check_user_details(rq[0], custUP);
cy.check_session(rq[1]);
cy.check_user_details(rq[2], custUP);
cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false);
cy.check_user_details(rq[4], custUP);
cy.check_user_details(rq[5], userDetailObj);
});
});
});
});
it("User data order test, async", () => {
hp.haltAndClearStorage(() => {
initMain();
Countly.q.push(['userData.set', "name", "John Doe"]);
Countly.q.push(['track_sessions']);
Countly.q.push(['userData.set', "name", "John Doe"]);
Countly.q.push(['add_event', eventObj]);
Countly.q.push(['userData.set', "name", "John Doe"]);
Countly.q.push(['user_details', userDetailObj]);
cy.fetch_local_event_queue().then((eq) => {
expect(eq.length).to.equal(0);
});
cy.wait(500).then(() => {
cy.fetch_local_request_queue(hp.appKey).then((rq) => {
expect(rq.length).to.equal(6);
cy.log(rq);
cy.check_user_details(rq[0], custUP);
cy.check_session(rq[1]);
cy.check_user_details(rq[2], custUP);
cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false);
cy.check_user_details(rq[4], custUP);
cy.check_user_details(rq[5], userDetailObj);
});
});
});
});
});
46 changes: 33 additions & 13 deletions lib/countly.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
backoffCount: "cly_hc_backoff_count",
consecutiveBackoffCount: "cly_hc_consecutive_backoff_count"
});
var SDK_VERSION = "25.4.3";
var SDK_VERSION = "25.4.4";
var SDK_NAME = "javascript_native_web";

// Using this on document.referrer would return an array with 17 elements in it. The 12th element (array[11]) would be the path we are looking for. Others would be things like password and such (use https://regex101.com/ to check more)
Expand Down Expand Up @@ -945,6 +945,7 @@
var _testModeTime = /*#__PURE__*/new WeakMap();
var _requestTimeoutDuration = /*#__PURE__*/new WeakMap();
var _contentFilterCallback = /*#__PURE__*/new WeakMap();
var _isProcessingAsyncFromUserDataSave = /*#__PURE__*/new WeakMap();
var _getAndSetServerConfig = /*#__PURE__*/new WeakMap();
var _populateServerConfig = /*#__PURE__*/new WeakMap();
var _initialize = /*#__PURE__*/new WeakMap();
Expand Down Expand Up @@ -1101,6 +1102,7 @@
_classPrivateFieldInitSpec(this, _testModeTime, void 0);
_classPrivateFieldInitSpec(this, _requestTimeoutDuration, void 0);
_classPrivateFieldInitSpec(this, _contentFilterCallback, void 0);
_classPrivateFieldInitSpec(this, _isProcessingAsyncFromUserDataSave, void 0);
_classPrivateFieldInitSpec(this, _getAndSetServerConfig, function () {
if (_this.device_id === "[CLY]_temp_id") {
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "server_config, Device ID is temporary, not fetching server config");
Expand Down Expand Up @@ -2059,6 +2061,7 @@
var req = {};
req.begin_session = 1;
req.metrics = JSON.stringify(_classPrivateFieldGet2(_getMetrics, _this).call(_this));
_this.userData.save(true); // ensure user data is saved before session start
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, req);
}
_classPrivateFieldGet2(_setValueInStorage, _this).call(_this, "cly_session", getTimestamp() + _classPrivateFieldGet2(_sessionCookieTimeout, _this) * 60);
Expand All @@ -2085,6 +2088,7 @@
return;
}
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "session_duration, Session extended: [" + sec + "]");
_this.userData.save(true); // ensure user data is saved before session update
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
session_duration: sec
});
Expand All @@ -2107,6 +2111,7 @@
_classPrivateFieldGet2(_reportViewDuration, _this).call(_this);
if (!_classPrivateFieldGet2(_useSessionCookie, _this) || force) {
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "end_session, Session ended");
_this.userData.save(true); // ensure user data is saved before session end
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
end_session: 1,
session_duration: sec
Expand Down Expand Up @@ -2267,6 +2272,9 @@
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.ERROR, "Adding event failed. Event must have a key property");
return;
}
if (!_classPrivateFieldGet2(_isProcessingAsyncFromUserDataSave, _this)) {
_this.userData.save(true); // ensure cached user data is saved before adding event
}
if (!event.count) {
event.count = 1;
}
Expand Down Expand Up @@ -2441,6 +2449,7 @@
user.byear = truncateSingleValue(user.byear, _classPrivateFieldGet2(_SCLimitValueSize, _this), "user_details", _classPrivateFieldGet2(_log, _this));
user.custom = truncateObject(user.custom, _classPrivateFieldGet2(_SCLimitKeyLength, _this), _classPrivateFieldGet2(_SCLimitValueSize, _this), _classPrivateFieldGet2(_SCLimitSegmentationValues, _this), "user_details", _classPrivateFieldGet2(_log, _this));
var props = ["name", "username", "email", "organization", "phone", "picture", "gender", "byear", "custom"];
_this.userData.save(); // ensure user data (and events) is saved before sending user details
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
user_details: JSON.stringify(createNewObjectFromProperties(user, props))
});
Expand Down Expand Up @@ -2627,20 +2636,30 @@
* Save changes made to user's custom properties object and send them to server
* @memberof Countly.userData
* */
save: function save() {
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, Saving changes to user's custom property");
if (_this.check_consent(featureEnums.USERS)) {
// process async queue before sending events
_classPrivateFieldGet2(_processAsyncQueue, _this).call(_this);
// flush events to event queue to prevent a drill issue
save: function save(forEvents) {
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, Saving changes to user's custom property. forEvents:[" + forEvents + "]");
if (!_this.check_consent(featureEnums.USERS) || Object.keys(_classPrivateFieldGet2(_customData, _this)).length === 0) {
return;
}
if (!forEvents) {
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.DEBUG, "[userData] save, flushing async queue and event queue before sending custom user data");
_classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, _this, true);
try {
// process async queue before sending events
_classPrivateFieldGet2(_processAsyncQueue, _this).call(_this);
} finally {
_classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, _this, false);
}

// flush events to request queue
_classPrivateFieldGet2(_sendEventsForced, _this).call(_this);
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "user_details, flushed the event queue");
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
user_details: JSON.stringify({
custom: _classPrivateFieldGet2(_customData, _this)
})
});
}
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, will send the following custom data to server: [" + JSON.stringify(_classPrivateFieldGet2(_customData, _this)) + "]");
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
user_details: JSON.stringify({
custom: _classPrivateFieldGet2(_customData, _this)
})
});
_classPrivateFieldSet2(_customData, _this, {});
}
});
Expand Down Expand Up @@ -6516,6 +6535,7 @@
_classPrivateFieldSet2(_SCBackoffDuration, this, 60); // 60 seconds
_classPrivateFieldSet2(_requestTimeoutDuration, this, 30000); // 30 seconds
_classPrivateFieldSet2(_contentFilterCallback, this, null);
_classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, this, false);
this.app_key = getConfig("app_key", _ob, null);
this.url = stripTrailingSlash(getConfig("url", _ob, ""));
this.serialize = getConfig("serialize", _ob, Countly.serialize);
Expand Down
Loading
Loading