Skip to content

Commit 8f570a1

Browse files
authored
Merge pull request #44 from Countly/up-changes
Up update 25.4.4
2 parents 80a7e99 + 62c0fda commit 8f570a1

File tree

6 files changed

+178
-12
lines changed

6 files changed

+178
-12
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/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("../../Countly.js");
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+
});

modules/Constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ var healthCheckCounterEnum = Object.freeze({
106106
consecutiveBackoffCount: "cly_hc_consecutive_backoff_count",
107107
});
108108

109-
var SDK_VERSION = "25.4.3";
109+
var SDK_VERSION = "25.4.4";
110110
var SDK_NAME = "javascript_native_web";
111111

112112
// 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)

modules/CountlyClass.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class CountlyClass {
126126
#testModeTime;
127127
#requestTimeoutDuration;
128128
#contentFilterCallback;
129+
#isProcessingAsyncFromUserDataSave;
129130
constructor(ob) {
130131
this.#self = this;
131132
this.#global = !Countly.i;
@@ -198,6 +199,7 @@ class CountlyClass {
198199
this.#SCBackoffDuration = 60; // 60 seconds
199200
this.#requestTimeoutDuration = 30000; // 30 seconds
200201
this.#contentFilterCallback = null;
202+
this.#isProcessingAsyncFromUserDataSave = false;
201203
this.app_key = getConfig("app_key", ob, null);
202204
this.url = stripTrailingSlash(getConfig("url", ob, ""));
203205
this.serialize = getConfig("serialize", ob, Countly.serialize);
@@ -1262,6 +1264,7 @@ class CountlyClass {
12621264
var req = {};
12631265
req.begin_session = 1;
12641266
req.metrics = JSON.stringify(this.#getMetrics());
1267+
this.userData.save(true); // ensure user data is saved before session start
12651268
this.#toRequestQueue(req);
12661269
}
12671270
this.#setValueInStorage("cly_session", getTimestamp() + (this.#sessionCookieTimeout * 60));
@@ -1292,6 +1295,7 @@ class CountlyClass {
12921295
}
12931296

12941297
this.#log(logLevelEnums.INFO, "session_duration, Session extended: [" + sec + "]");
1298+
this.userData.save(true); // ensure user data is saved before session update
12951299
this.#toRequestQueue({ session_duration: sec });
12961300
this.#extendSession();
12971301
};
@@ -1313,6 +1317,7 @@ class CountlyClass {
13131317
this.#reportViewDuration();
13141318
if (!this.#useSessionCookie || force) {
13151319
this.#log(logLevelEnums.INFO, "end_session, Session ended");
1320+
this.userData.save(true); // ensure user data is saved before session end
13161321
this.#toRequestQueue({ end_session: 1, session_duration: sec });
13171322
}
13181323
else {
@@ -1478,6 +1483,10 @@ class CountlyClass {
14781483
return;
14791484
}
14801485

1486+
if (!this.#isProcessingAsyncFromUserDataSave) {
1487+
this.userData.save(true); // ensure cached user data is saved before adding event
1488+
}
1489+
14811490
if (!event.count) {
14821491
event.count = 1;
14831492
}
@@ -1654,6 +1663,7 @@ class CountlyClass {
16541663
user.byear = truncateSingleValue(user.byear, this.#SCLimitValueSize , "user_details", this.#log);
16551664
user.custom = truncateObject(user.custom, this.#SCLimitKeyLength, this.#SCLimitValueSize , this.#SCLimitSegmentationValues, "user_details", this.#log);
16561665
var props = ["name", "username", "email", "organization", "phone", "picture", "gender", "byear", "custom"];
1666+
this.userData.save(); // ensure user data (and events) is saved before sending user details
16571667
this.#toRequestQueue({ user_details: JSON.stringify(createNewObjectFromProperties(user, props)) });
16581668
}
16591669
};
@@ -1841,16 +1851,29 @@ class CountlyClass {
18411851
* Save changes made to user's custom properties object and send them to server
18421852
* @memberof Countly.userData
18431853
* */
1844-
save: () => {
1845-
this.#log(logLevelEnums.INFO, "[userData] save, Saving changes to user's custom property");
1846-
if (this.check_consent(featureEnums.USERS)) {
1847-
// process async queue before sending events
1848-
this.#processAsyncQueue();
1849-
// flush events to event queue to prevent a drill issue
1854+
save: (forEvents) => {
1855+
this.#log(logLevelEnums.INFO, "[userData] save, Saving changes to user's custom property. forEvents:[" + forEvents + "]");
1856+
if (!this.check_consent(featureEnums.USERS) || Object.keys(this.#customData).length === 0) {
1857+
return;
1858+
}
1859+
1860+
if (!forEvents) {
1861+
this.#log(logLevelEnums.DEBUG, "[userData] save, flushing async queue and event queue before sending custom user data");
1862+
this.#isProcessingAsyncFromUserDataSave = true;
1863+
try {
1864+
// process async queue before sending events
1865+
this.#processAsyncQueue();
1866+
}
1867+
finally {
1868+
this.#isProcessingAsyncFromUserDataSave = false;
1869+
}
1870+
1871+
// flush events to request queue
18501872
this.#sendEventsForced();
1851-
this.#log(logLevelEnums.INFO, "user_details, flushed the event queue");
1852-
this.#toRequestQueue({ user_details: JSON.stringify({ custom: this.#customData }) });
18531873
}
1874+
1875+
this.#log(logLevelEnums.INFO, "[userData] save, will send the following custom data to server: [" + JSON.stringify(this.#customData) + "]");
1876+
this.#toRequestQueue({ user_details: JSON.stringify({ custom: this.#customData }) });
18541877
this.#customData = {};
18551878
}
18561879
};

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "countly-sdk-js",
3-
"version": "25.4.3",
3+
"version": "25.4.4",
44
"description": "Countly JavaScript SDK",
55
"type": "module",
66
"main": "Countly.js",

0 commit comments

Comments
 (0)