diff --git a/CHANGELOG.md b/CHANGELOG.md index f5e6bd8e..7d71fcb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## 24.4.1 - Added types for the SDK +- Added a new method `set_id(newDeviceId)` for managing device id changes according to the device ID Type ## 24.4.0 ! Minor breaking change ! For implementations using `salt` the browser compatability is tied to SubtleCrypto's `digest` method support diff --git a/cypress/integration/bridge_utils.js b/cypress/integration/bridge_utils.js index 03495d34..dda162b7 100644 --- a/cypress/integration/bridge_utils.js +++ b/cypress/integration/bridge_utils.js @@ -16,7 +16,7 @@ function initMain(name, version) { } const SDK_NAME = "javascript_native_web"; -const SDK_VERSION = "24.4.0"; +const SDK_VERSION = "24.4.1"; // tests describe("Bridged SDK Utilities Tests", () => { diff --git a/cypress/integration/device_id_change.js b/cypress/integration/device_id_change.js index 3ada7d90..9cc3a269 100644 --- a/cypress/integration/device_id_change.js +++ b/cypress/integration/device_id_change.js @@ -145,6 +145,14 @@ describe("Device ID change tests ", ()=>{ }); }); }); + it("Check init time temp mode with set_id", () => { + hp.haltAndClearStorage(() => { + initMain(true); // init in offline mode + testDeviceIdInReqs(() => { + Countly.set_id("new ID"); + }); + }); + }); // ======================================== // default init configuration tests @@ -209,4 +217,60 @@ describe("Device ID change tests ", ()=>{ }); }); }); -}); \ No newline at end of file +}); + +describe("Set ID change tests ", () => { + it("set_id should be non merge as there was dev provided id", () => { + hp.haltAndClearStorage(() => { + Countly.init({ + app_key: "YOUR_APP_KEY", + url: "https://your.domain.count.ly", + test_mode: true, + debug: true, + device_id: "old ID" + }); + Countly.add_event(eventObj("1")); // record an event. + cy.wait(500); // wait for the request to be sent + cy.fetch_local_request_queue().then((eq) => { + expect(eq[0].device_id).to.equal("old ID"); + Countly.set_id("new ID"); + Countly.add_event(eventObj("2")); // record another event + cy.wait(500); // wait for the request to be sent + cy.fetch_local_request_queue().then((eq2) => { + expect(eq2.length).to.equal(3); // no merge request + expect(eq2[0].device_id).to.equal("old ID"); + expect(eq2[0].events).to.contains("\"key\":\"1\""); + expect(eq2[1].device_id).to.equal("new ID"); + expect(eq2[1].begin_session).to.equal(1); + expect(eq2[2].device_id).to.equal("new ID"); + expect(eq2[2].events).to.contains("\"key\":\"2\""); + }); + }); + }); + }); + it("set_id should be merge as there was sdk generated id", () => { + hp.haltAndClearStorage(() => { + initMain(false); // init normally + Countly.add_event(eventObj("1")); // record an event. + cy.wait(500); // wait for the request to be sent + let generatedID; + cy.fetch_local_request_queue().then((eq) => { + cy.log(eq); + generatedID = eq[0].device_id; // get the new id from first item in the queue + Countly.set_id("new ID"); + Countly.add_event(eventObj("2")); // record another event + cy.wait(500); // wait for the request to be sent + cy.fetch_local_request_queue().then((eq2) => { + cy.log(eq2); + expect(eq2.length).to.equal(3); // merge request + expect(eq2[0].device_id).to.equal(generatedID); + expect(eq2[0].events).to.contains("\"key\":\"1\""); + expect(eq2[1].device_id).to.equal("new ID"); + expect(eq2[1].old_device_id).to.equal(generatedID); + expect(eq2[2].device_id).to.equal("new ID"); + expect(eq2[2].events).to.contains("\"key\":\"2\""); + }); + }); + }); + }); +}); diff --git a/lib/countly.d.ts b/lib/countly.d.ts index 5860cf2d..58a32189 100644 --- a/lib/countly.d.ts +++ b/lib/countly.d.ts @@ -119,12 +119,18 @@ declare module "countly-sdk-web" { function end_session(sec: number, force: boolean): void; /** - * Change current user/device id + * Change current user/device id (use set_id instead if you are not sure about the merge operation) * @param {string} newId - new user/device ID to use. Must be a non-empty string value. Invalid values (like null, empty string or undefined) will be rejected * @param {boolean} merge - move data from old ID to new ID on server */ function change_id(newId: string, merge: boolean): void; + /** + * Changes the current device ID according to the device ID type (the preffered method) + * @param {string} newId - new user/device ID to use. Must be a non-empty string value. Invalid values (like null, empty string or undefined) will be rejected + */ + function set_id(newId: string): void; + /** * Report custom event * @param {Object} event - Countly {@link Event} object diff --git a/lib/countly.js b/lib/countly.js index b9451f51..88f70657 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -196,7 +196,7 @@ statusCode: "cly_hc_status_code", errorMessage: "cly_hc_error_message" }); - var SDK_VERSION = "24.4.0"; + var SDK_VERSION = "24.4.1"; 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) @@ -1752,17 +1752,33 @@ }; /** - * Change current user/device id + * Changes the current device ID according to the device ID type (the preffered method) + * @param {string} newId - new user/device ID to use. Must be a non-empty string value. Invalid values (like null, empty string or undefined) will be rejected + * */ + this.set_id = function (newId) { + log(logLevelEnums.INFO, "set_id, Changing the device ID to:[" + newId + "]"); + if (newId == null || newId === "") { + log(logLevelEnums.WARNING, "set_id, The provided device is not a valid ID"); + return; + } + if (deviceIdType === DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED) { + /*change ID without merge as current ID is Dev supplied, so not first login*/ + this.change_id(newId, false); + } else { + /*change ID with merge as current ID is not Dev supplied*/ + this.change_id(newId, true); + } + }; + + /** + * Change current user/device id (use set_id instead if you are not sure about the merge operation) * @param {string} newId - new user/device ID to use. Must be a non-empty string value. Invalid values (like null, empty string or undefined) will be rejected * @param {boolean} merge - move data from old ID to new ID on server * */ this.change_id = function (newId, merge) { - log(logLevelEnums.INFO, "change_id, Changing the ID"); - if (merge) { - log(logLevelEnums.INFO, "change_id, Will merge the IDs"); - } + log(logLevelEnums.INFO, "change_id, Changing the device ID to: [" + newId + "] with merge:[" + merge + "]"); if (!newId || typeof newId !== "string" || newId.length === 0) { - log(logLevelEnums.ERROR, "change_id, The provided ID: [" + newId + "] is not a valid ID"); + log(logLevelEnums.WARNING, "change_id, The provided device ID is not a valid ID"); return; } if (offlineMode) { @@ -1772,41 +1788,43 @@ } // eqeq is used here since we want to catch number to string checks too. type conversion might happen at a new init // eslint-disable-next-line eqeqeq - if (this.device_id != newId) { - if (!merge) { - // process async queue before sending events - processAsyncQueue(); - // empty event queue - sendEventsForced(); - // end current session - this.end_session(null, true); - // clear timed events - timedEvents = {}; - // clear all consents - this.remove_consent_internal(Countly.features, false); - } - var oldId = this.device_id; - this.device_id = newId; - self.device_id = this.device_id; - deviceIdType = DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED; - setValueInStorage("cly_id", this.device_id); - setValueInStorage("cly_id_type", DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED); - log(logLevelEnums.INFO, "change_id, Changing ID from:[" + oldId + "] to [" + newId + "]"); - if (merge) { - // no consent check here since 21.11.0 - toRequestQueue({ - old_device_id: oldId - }); - } else { - // start new session for new ID - this.begin_session(!autoExtend, true); - } - // if init time remote config was enabled with a callback function, remove currently stored remote configs and fetch remote config again - if (this.remote_config) { - remoteConfigs = {}; - setValueInStorage("cly_remote_configs", remoteConfigs); - this.fetch_remote_config(this.remote_config); - } + if (this.device_id == newId) { + log(logLevelEnums.DEBUG, "change_id, Provided device ID is equal to the current device ID. Aborting."); + return; + } + if (!merge) { + // process async queue before sending events + processAsyncQueue(); + // empty event queue + sendEventsForced(); + // end current session + this.end_session(null, true); + // clear timed events + timedEvents = {}; + // clear all consents + this.remove_consent_internal(Countly.features, false); + } + var oldId = this.device_id; + this.device_id = newId; + self.device_id = this.device_id; + deviceIdType = DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED; + setValueInStorage("cly_id", this.device_id); + setValueInStorage("cly_id_type", DeviceIdTypeInternalEnums.DEVELOPER_SUPPLIED); + log(logLevelEnums.INFO, "change_id, Changing ID from:[" + oldId + "] to [" + newId + "]"); + if (merge) { + // no consent check here since 21.11.0 + toRequestQueue({ + old_device_id: oldId + }); + } else { + // start new session for new ID TODO: check this when no session tracking is enabled + this.begin_session(!autoExtend, true); + } + // if init time remote config was enabled with a callback function, remove currently stored remote configs and fetch remote config again + if (this.remote_config) { + remoteConfigs = {}; + setValueInStorage("cly_remote_configs", remoteConfigs); + this.fetch_remote_config(this.remote_config); } }; @@ -5254,7 +5272,6 @@ isResponseValidBroad: isResponseValidBroad, secureRandom: secureRandom, log: log, - calculateChecksum: calculateChecksum, checkIfLoggingIsOn: checkIfLoggingIsOn, getMetrics: getMetrics, getUA: getUA, @@ -5262,6 +5279,7 @@ generateUUID: generateUUID, sendEventsForced: sendEventsForced, isUUID: isUUID, + calculateChecksum: calculateChecksum, isReferrerUsable: isReferrerUsable, getId: getStoredIdOrGenerateId, heartBeat: heartBeat, diff --git a/package.json b/package.json index 5ed38bb0..e2e62aaa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "countly-sdk-web", - "version": "24.4.0", + "version": "24.4.1", "description": "Countly Web SDK", "main": "lib/countly.js", "types": "lib/countly.d.ts",