Skip to content

Commit 02f0dc7

Browse files
authored
Merge pull request #599 from Countly/up-update
UP order update
2 parents 9470148 + 5dcda7b commit 02f0dc7

File tree

7 files changed

+373
-209
lines changed

7 files changed

+373
-209
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 25.4.4
2+
3+
- Improved user property recording order with respect to sessions and events.
4+
15
## 25.4.3
26

37
- Added filtering capability to `content` interface through `enterContentZone(contentFilterCallback)`.

cypress/e2e/bridged_utils.cy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function initMain(name, version) {
1515
}
1616

1717
const SDK_NAME = "javascript_native_web";
18-
const SDK_VERSION = "25.4.3";
18+
const SDK_VERSION = "25.4.4";
1919

2020
// tests
2121
describe("Bridged SDK Utilities Tests", () => {

cypress/e2e/up_order.cy.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/* eslint-disable cypress/no-unnecessary-waiting */
2+
/* eslint-disable require-jsdoc */
3+
var Countly = require("../../lib/countly");
4+
var hp = require("../support/helper");
5+
6+
Countly.q = Countly.q || [];
7+
8+
function initMain() {
9+
Countly.init({
10+
app_key: "YOUR_APP_KEY",
11+
url: "https://your.domain.count.ly",
12+
test_mode_eq: true,
13+
test_mode: true
14+
});
15+
}
16+
17+
const userDetailObj = hp.userDetailObj;
18+
19+
// an event object to use
20+
const eventObj = {
21+
key: "in_app_purchase",
22+
count: 3,
23+
sum: 2.97,
24+
dur: 300,
25+
segmentation: {
26+
app_version: "1.0",
27+
country: "Tahiti"
28+
}
29+
};
30+
const custUP = {
31+
custom: { "name": "John Doe"}
32+
}
33+
34+
describe("User properties order test", () => {
35+
it("User details order test", () => {
36+
hp.haltAndClearStorage(() => {
37+
initMain();
38+
Countly.begin_session();
39+
Countly.add_event(eventObj);
40+
Countly.user_details(userDetailObj);
41+
Countly.add_event(eventObj);
42+
Countly.add_event(eventObj);
43+
Countly.user_details(userDetailObj);
44+
cy.fetch_local_event_queue().then((eq) => {
45+
expect(eq.length).to.equal(0);
46+
});
47+
cy.wait(500).then(() => {
48+
cy.fetch_local_request_queue(hp.appKey).then((rq) => {
49+
expect(rq.length).to.equal(5);
50+
cy.log(rq);
51+
cy.check_session(rq[0]);
52+
cy.check_event(JSON.parse(rq[1].events)[1], eventObj, undefined, false);
53+
cy.check_user_details(rq[2], userDetailObj);
54+
cy.check_event(JSON.parse(rq[3].events)[0], eventObj, undefined, false);
55+
cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false);
56+
cy.check_user_details(rq[4], userDetailObj);
57+
});
58+
});
59+
});
60+
});
61+
it("User details order test, async", () => {
62+
hp.haltAndClearStorage(() => {
63+
initMain();
64+
Countly.q.push(['track_sessions']);
65+
Countly.q.push(['add_event', eventObj]);
66+
Countly.q.push(['user_details', userDetailObj]);
67+
Countly.q.push(['add_event', eventObj]);
68+
Countly.q.push(['add_event', eventObj]);
69+
Countly.q.push(['user_details', userDetailObj]);
70+
cy.fetch_local_event_queue().then((eq) => {
71+
expect(eq.length).to.equal(0);
72+
});
73+
cy.wait(500).then(() => {
74+
cy.fetch_local_request_queue(hp.appKey).then((rq) => {
75+
expect(rq.length).to.equal(5);
76+
cy.log(rq);
77+
cy.check_session(rq[0]);
78+
cy.check_event(JSON.parse(rq[1].events)[1], eventObj, undefined, false);
79+
cy.check_user_details(rq[2], userDetailObj);
80+
cy.check_event(JSON.parse(rq[3].events)[0], eventObj, undefined, false);
81+
cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false);
82+
cy.check_user_details(rq[4], userDetailObj);
83+
});
84+
});
85+
});
86+
});
87+
it("User data order test", () => {
88+
hp.haltAndClearStorage(() => {
89+
initMain();
90+
Countly.userData.set("name", "John Doe");
91+
Countly.begin_session();
92+
Countly.userData.set("name", "John Doe");
93+
Countly.add_event(eventObj);
94+
Countly.userData.set("name", "John Doe");
95+
Countly.user_details(userDetailObj);
96+
cy.fetch_local_event_queue().then((eq) => {
97+
expect(eq.length).to.equal(0);
98+
});
99+
cy.wait(500).then(() => {
100+
cy.fetch_local_request_queue(hp.appKey).then((rq) => {
101+
expect(rq.length).to.equal(6);
102+
cy.log(rq);
103+
cy.check_user_details(rq[0], custUP);
104+
cy.check_session(rq[1]);
105+
cy.check_user_details(rq[2], custUP);
106+
cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false);
107+
cy.check_user_details(rq[4], custUP);
108+
cy.check_user_details(rq[5], userDetailObj);
109+
});
110+
});
111+
});
112+
});
113+
it("User data order test, async", () => {
114+
hp.haltAndClearStorage(() => {
115+
initMain();
116+
Countly.q.push(['userData.set', "name", "John Doe"]);
117+
Countly.q.push(['track_sessions']);
118+
Countly.q.push(['userData.set', "name", "John Doe"]);
119+
Countly.q.push(['add_event', eventObj]);
120+
Countly.q.push(['userData.set', "name", "John Doe"]);
121+
Countly.q.push(['user_details', userDetailObj]);
122+
cy.fetch_local_event_queue().then((eq) => {
123+
expect(eq.length).to.equal(0);
124+
});
125+
cy.wait(500).then(() => {
126+
cy.fetch_local_request_queue(hp.appKey).then((rq) => {
127+
expect(rq.length).to.equal(6);
128+
cy.log(rq);
129+
cy.check_user_details(rq[0], custUP);
130+
cy.check_session(rq[1]);
131+
cy.check_user_details(rq[2], custUP);
132+
cy.check_event(JSON.parse(rq[3].events)[1], eventObj, undefined, false);
133+
cy.check_user_details(rq[4], custUP);
134+
cy.check_user_details(rq[5], userDetailObj);
135+
});
136+
});
137+
});
138+
});
139+
});

lib/countly.js

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@
217217
backoffCount: "cly_hc_backoff_count",
218218
consecutiveBackoffCount: "cly_hc_consecutive_backoff_count"
219219
});
220-
var SDK_VERSION = "25.4.3";
220+
var SDK_VERSION = "25.4.4";
221221
var SDK_NAME = "javascript_native_web";
222222

223223
// 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)
@@ -945,6 +945,7 @@
945945
var _testModeTime = /*#__PURE__*/new WeakMap();
946946
var _requestTimeoutDuration = /*#__PURE__*/new WeakMap();
947947
var _contentFilterCallback = /*#__PURE__*/new WeakMap();
948+
var _isProcessingAsyncFromUserDataSave = /*#__PURE__*/new WeakMap();
948949
var _getAndSetServerConfig = /*#__PURE__*/new WeakMap();
949950
var _populateServerConfig = /*#__PURE__*/new WeakMap();
950951
var _initialize = /*#__PURE__*/new WeakMap();
@@ -1101,6 +1102,7 @@
11011102
_classPrivateFieldInitSpec(this, _testModeTime, void 0);
11021103
_classPrivateFieldInitSpec(this, _requestTimeoutDuration, void 0);
11031104
_classPrivateFieldInitSpec(this, _contentFilterCallback, void 0);
1105+
_classPrivateFieldInitSpec(this, _isProcessingAsyncFromUserDataSave, void 0);
11041106
_classPrivateFieldInitSpec(this, _getAndSetServerConfig, function () {
11051107
if (_this.device_id === "[CLY]_temp_id") {
11061108
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "server_config, Device ID is temporary, not fetching server config");
@@ -2059,6 +2061,7 @@
20592061
var req = {};
20602062
req.begin_session = 1;
20612063
req.metrics = JSON.stringify(_classPrivateFieldGet2(_getMetrics, _this).call(_this));
2064+
_this.userData.save(true); // ensure user data is saved before session start
20622065
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, req);
20632066
}
20642067
_classPrivateFieldGet2(_setValueInStorage, _this).call(_this, "cly_session", getTimestamp() + _classPrivateFieldGet2(_sessionCookieTimeout, _this) * 60);
@@ -2085,6 +2088,7 @@
20852088
return;
20862089
}
20872090
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "session_duration, Session extended: [" + sec + "]");
2091+
_this.userData.save(true); // ensure user data is saved before session update
20882092
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
20892093
session_duration: sec
20902094
});
@@ -2107,6 +2111,7 @@
21072111
_classPrivateFieldGet2(_reportViewDuration, _this).call(_this);
21082112
if (!_classPrivateFieldGet2(_useSessionCookie, _this) || force) {
21092113
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "end_session, Session ended");
2114+
_this.userData.save(true); // ensure user data is saved before session end
21102115
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
21112116
end_session: 1,
21122117
session_duration: sec
@@ -2267,6 +2272,9 @@
22672272
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.ERROR, "Adding event failed. Event must have a key property");
22682273
return;
22692274
}
2275+
if (!_classPrivateFieldGet2(_isProcessingAsyncFromUserDataSave, _this)) {
2276+
_this.userData.save(true); // ensure cached user data is saved before adding event
2277+
}
22702278
if (!event.count) {
22712279
event.count = 1;
22722280
}
@@ -2441,6 +2449,7 @@
24412449
user.byear = truncateSingleValue(user.byear, _classPrivateFieldGet2(_SCLimitValueSize, _this), "user_details", _classPrivateFieldGet2(_log, _this));
24422450
user.custom = truncateObject(user.custom, _classPrivateFieldGet2(_SCLimitKeyLength, _this), _classPrivateFieldGet2(_SCLimitValueSize, _this), _classPrivateFieldGet2(_SCLimitSegmentationValues, _this), "user_details", _classPrivateFieldGet2(_log, _this));
24432451
var props = ["name", "username", "email", "organization", "phone", "picture", "gender", "byear", "custom"];
2452+
_this.userData.save(); // ensure user data (and events) is saved before sending user details
24442453
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
24452454
user_details: JSON.stringify(createNewObjectFromProperties(user, props))
24462455
});
@@ -2627,20 +2636,30 @@
26272636
* Save changes made to user's custom properties object and send them to server
26282637
* @memberof Countly.userData
26292638
* */
2630-
save: function save() {
2631-
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, Saving changes to user's custom property");
2632-
if (_this.check_consent(featureEnums.USERS)) {
2633-
// process async queue before sending events
2634-
_classPrivateFieldGet2(_processAsyncQueue, _this).call(_this);
2635-
// flush events to event queue to prevent a drill issue
2639+
save: function save(forEvents) {
2640+
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, Saving changes to user's custom property. forEvents:[" + forEvents + "]");
2641+
if (!_this.check_consent(featureEnums.USERS) || Object.keys(_classPrivateFieldGet2(_customData, _this)).length === 0) {
2642+
return;
2643+
}
2644+
if (!forEvents) {
2645+
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.DEBUG, "[userData] save, flushing async queue and event queue before sending custom user data");
2646+
_classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, _this, true);
2647+
try {
2648+
// process async queue before sending events
2649+
_classPrivateFieldGet2(_processAsyncQueue, _this).call(_this);
2650+
} finally {
2651+
_classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, _this, false);
2652+
}
2653+
2654+
// flush events to request queue
26362655
_classPrivateFieldGet2(_sendEventsForced, _this).call(_this);
2637-
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "user_details, flushed the event queue");
2638-
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
2639-
user_details: JSON.stringify({
2640-
custom: _classPrivateFieldGet2(_customData, _this)
2641-
})
2642-
});
26432656
}
2657+
_classPrivateFieldGet2(_log, _this).call(_this, logLevelEnums.INFO, "[userData] save, will send the following custom data to server: [" + JSON.stringify(_classPrivateFieldGet2(_customData, _this)) + "]");
2658+
_classPrivateFieldGet2(_toRequestQueue, _this).call(_this, {
2659+
user_details: JSON.stringify({
2660+
custom: _classPrivateFieldGet2(_customData, _this)
2661+
})
2662+
});
26442663
_classPrivateFieldSet2(_customData, _this, {});
26452664
}
26462665
});
@@ -6516,6 +6535,7 @@
65166535
_classPrivateFieldSet2(_SCBackoffDuration, this, 60); // 60 seconds
65176536
_classPrivateFieldSet2(_requestTimeoutDuration, this, 30000); // 30 seconds
65186537
_classPrivateFieldSet2(_contentFilterCallback, this, null);
6538+
_classPrivateFieldSet2(_isProcessingAsyncFromUserDataSave, this, false);
65196539
this.app_key = getConfig("app_key", _ob, null);
65206540
this.url = stripTrailingSlash(getConfig("url", _ob, ""));
65216541
this.serialize = getConfig("serialize", _ob, Countly.serialize);

0 commit comments

Comments
 (0)