diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f3de83..d62c898 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 24.10.1 +- Added a new method `set_id(newDeviceId)` for managing device ID changes according to the device ID Type +- Added `DeviceIdType` enums to be used to evaluate the device ID type. +- Added reserved keys for user properties + ## 24.10.0 - Default max segmentation value count changed from 30 to 100 - Mitigated an issue where SDK could create an unintended dump file diff --git a/lib/countly-bulk-user.js b/lib/countly-bulk-user.js index 0f45a42..45e3d37 100644 --- a/lib/countly-bulk-user.js +++ b/lib/countly-bulk-user.js @@ -602,7 +602,10 @@ function CountlyBulkUser(conf) { var change_custom_property = function(key, value, mod) { key = cc.truncateSingleValue(key, conf.maxKeyLength, "change_custom_property"); value = cc.truncateSingleValue(value, conf.maxValueSize, "change_custom_property"); - + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + cc.log(cc.logLevelEnums.ERROR, "change_custom_property, Provided key is not allowed."); + return; + } if (!customData[key]) { customData[key] = {}; } diff --git a/lib/countly-bulk.js b/lib/countly-bulk.js index a7ecc0e..2670127 100644 --- a/lib/countly-bulk.js +++ b/lib/countly-bulk.js @@ -59,7 +59,7 @@ CountlyBulk.StorageTypes = cc.storageTypeEnums; * }); */ function CountlyBulk(conf) { - var SDK_VERSION = "24.10.0"; + var SDK_VERSION = "24.10.1"; var SDK_NAME = "javascript_native_nodejs_bulk"; var empty_queue_callback = null; diff --git a/lib/countly.js b/lib/countly.js index 761ca7a..50d7ad6 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -30,9 +30,10 @@ var CountlyStorage = require("./countly-storage"); var Countly = {}; Countly.StorageTypes = cc.storageTypeEnums; +Countly.DeviceIdType = cc.deviceIdTypeEnums; Countly.Bulk = Bulk; (function() { - var SDK_VERSION = "24.10.0"; + var SDK_VERSION = "24.10.1"; var SDK_NAME = "javascript_native_nodejs"; var inited = false; @@ -627,6 +628,26 @@ Countly.Bulk = Bulk; return Countly.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 + * */ + Countly.set_id = function(newId) { + cc.log(cc.logLevelEnums.INFO, `set_id, Changing the device ID to: [${newId}]`); + if (newId === null || newId === undefined || newId === "" || typeof newId !== "string") { + cc.log(cc.logLevelEnums.WARNING, "set_id, The provided id is not a valid ID"); + return; + } + if (Countly.get_device_id_type() === cc.deviceIdTypeEnums.DEVELOPER_SUPPLIED) { + // change ID without merge as current ID is Dev supplied, so not first login + Countly.change_id(newId, false); + } + else { + // change ID with merge as current ID is not Dev supplied*/ + Countly.change_id(newId, true); + } + }; + /** * Change current user/device id * @param {string} newId - new user/device ID to use @@ -844,6 +865,10 @@ Countly.Bulk = Bulk; var change_custom_property = function(key, value, mod) { key = cc.truncateSingleValue(key, Countly.maxKeyLength, "change_custom_property", Countly.debug); value = cc.truncateSingleValue(value, Countly.maxValueSize, "change_custom_property", Countly.debug); + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + cc.log(cc.logLevelEnums.ERROR, "change_custom_property, Provided key is not allowed."); + return; + } if (Countly.check_consent("users")) { if (!customData[key]) { diff --git a/package.json b/package.json index c35b9f7..4b2b870 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "countly-sdk-nodejs", - "version": "24.10.0", + "version": "24.10.1", "description": "Countly NodeJS SDK", "main": "lib/countly.js", "directories": { diff --git a/test/helpers/helper_functions.js b/test/helpers/helper_functions.js index d8340f6..996f971 100644 --- a/test/helpers/helper_functions.js +++ b/test/helpers/helper_functions.js @@ -284,4 +284,5 @@ module.exports = { validateUserDetails, viewEventValidator, doesFileStoragePathsExist, + requestBaseParamValidator, }; \ No newline at end of file diff --git a/test/tests_device_id.js b/test/tests_device_id.js new file mode 100644 index 0000000..db719e8 --- /dev/null +++ b/test/tests_device_id.js @@ -0,0 +1,102 @@ +/* eslint-disable no-console */ +var assert = require("assert"); +var Countly = require("../lib/countly"); +var cc = require("../lib/countly-common"); +var hp = require("./helpers/helper_functions"); + +function initMain(deviceId, eraseID) { + Countly.init({ + app_key: "YOUR_APP_KEY", + url: "https://try.count.ly", + device_id: deviceId, + max_events: -1, + // debug: true, + clear_stored_device_id: eraseID, + }); +} +function validateSdkGeneratedId(providedDeviceId) { + assert.ok(providedDeviceId); + assert.equal(providedDeviceId.length, 36); + assert.ok(cc.isUUID(providedDeviceId)); + assert.equal(Countly.get_device_id(), providedDeviceId); + assert.equal(Countly.get_device_id_type(), Countly.DeviceIdType.SDK_GENERATED); +} + +function validateDeveloperSuppliedId(providedDeviceId) { + assert.equal(Countly.get_device_id_type(), Countly.DeviceIdType.DEVELOPER_SUPPLIED); + assert.equal(Countly.get_device_id(), providedDeviceId); +} + +describe("Device ID tests", () => { + beforeEach(async() => { + await hp.clearStorage(); + }); + + it("1- set_id with SDK generated to developer supplied", (done) => { + // initialize SDK + initMain(undefined); + validateSdkGeneratedId(Countly.get_device_id()); + var oldId = Countly.get_device_id(); + Countly.set_id("ID"); + validateDeveloperSuppliedId("ID"); + setTimeout(() => { + // validate that merge request is generated + var RQ = hp.readRequestQueue(); + assert.equal(RQ.length, 1); + hp.requestBaseParamValidator(RQ[0]); + assert.equal(RQ[0].old_device_id, oldId); + done(); + }, hp.sWait); + }); + + it("2- set_id with developer supplied to developer supplied", (done) => { + // initialize SDK + initMain("ID2"); + validateDeveloperSuppliedId("ID2"); + Countly.set_id("ID"); + validateDeveloperSuppliedId("ID"); + setTimeout(() => { + // validate that no merge request is generated and the existing request is begin session + var RQ = hp.readRequestQueue(); + assert.equal(RQ.length, 1); + hp.sessionRequestValidator(RQ[0]); + done(); + }, hp.sWait); + }); + + it("3- set_id with same custom id", (done) => { + // initialize SDK + initMain("ID"); + validateDeveloperSuppliedId("ID"); + Countly.set_id("ID"); + validateDeveloperSuppliedId("ID"); + done(); + }); + + it("4- set_id with same sdk generated id", (done) => { + // initialize SDK + initMain(undefined); + var id = Countly.get_device_id(); + validateSdkGeneratedId(id); + Countly.set_id(id); + // so that the type is not converted to developer_supplied + validateSdkGeneratedId(id); + done(); + }); + + it("5- set_id with invalid ids", (done) => { + // initialize SDK + initMain(undefined); + var id = Countly.get_device_id(); + validateSdkGeneratedId(id); + Countly.set_id(undefined); + validateSdkGeneratedId(id); + + Countly.set_id(null); + validateSdkGeneratedId(id); + + Countly.set_id(""); + validateSdkGeneratedId(id); + done(); + }); +}); diff --git a/test/tests_device_id_type.js b/test/tests_device_id_type.js index 650f1b5..4e7c683 100644 --- a/test/tests_device_id_type.js +++ b/test/tests_device_id_type.js @@ -58,7 +58,7 @@ describe("Device ID type tests", () => { beforeEach(async() => { await hp.clearStorage(); }); - it("1.Generated device ID", (done) => { + it("1- Generated device ID", (done) => { // initialize SDK initMain(undefined); Countly.begin_session(); @@ -71,7 +71,8 @@ describe("Device ID type tests", () => { done(); }, hp.sWait); }); - it("2.Developer supplied device ID", (done) => { + + it("2- Developer supplied device ID", (done) => { // initialize SDK initMain("ID"); Countly.begin_session(); @@ -84,7 +85,8 @@ describe("Device ID type tests", () => { done(); }, hp.sWait); }); - it("3.With stored dev ID and no new ID", (done) => { + + it("3- With stored dev ID and no new ID", (done) => { // initialize SDK initMain("ID"); Countly.begin_session(); @@ -105,7 +107,8 @@ describe("Device ID type tests", () => { }, hp.sWait); }, hp.sWait); }); - it("4.With stored dev ID and with new ID", (done) => { + + it("4- With stored dev ID and with new ID", (done) => { // initialize SDK initMain("ID"); Countly.begin_session(); @@ -126,7 +129,8 @@ describe("Device ID type tests", () => { }, hp.sWait); }, hp.sWait); }); - it("5.With stored generated ID and no new ID", (done) => { + + it("5- With stored generated ID and no new ID", (done) => { // initialize SDK initMain(undefined); Countly.begin_session(); @@ -149,7 +153,8 @@ describe("Device ID type tests", () => { }, hp.sWait); }, hp.sWait); }); - it("6.With stored generated ID and with new ID", (done) => { + + it("6- With stored generated ID and with new ID", (done) => { // initialize SDK initMain(undefined); Countly.begin_session(); @@ -172,7 +177,8 @@ describe("Device ID type tests", () => { }, hp.sWait); }, hp.sWait); }); - it("7.With stored dev ID and no new ID, flag set", (done) => { + + it("7- With stored dev ID and no new ID, flag set", (done) => { // initialize SDK initMain("ID"); Countly.begin_session(); @@ -194,7 +200,7 @@ describe("Device ID type tests", () => { }, hp.sWait); }); - it("8.With stored dev ID and with new ID, flag set", (done) => { + it("8- With stored dev ID and with new ID, flag set", (done) => { setTimeout(() => { // initialize SDK initMain("ID"); @@ -217,7 +223,8 @@ describe("Device ID type tests", () => { }, hp.lWait); }, hp.lWait); }); - it("9.With stored sdk ID and no new ID, flag set", (done) => { + + it("9- With stored sdk ID and no new ID, flag set", (done) => { // initialize SDK initMain(undefined); Countly.begin_session(); @@ -241,7 +248,7 @@ describe("Device ID type tests", () => { }, hp.sWait); }); - it("10.With stored sdk ID and with new ID, flag set", (done) => { + it("10- With stored sdk ID and with new ID, flag set", (done) => { // initialize SDK initMain(undefined); Countly.begin_session(); @@ -264,7 +271,8 @@ describe("Device ID type tests", () => { }, hp.sWait); }, hp.sWait); }); - it("11.Change generated device ID", (done) => { + + it("11- Change generated device ID", (done) => { // initialize SDK initMain(undefined); Countly.change_id("changedID"); @@ -280,7 +288,8 @@ describe("Device ID type tests", () => { }, hp.sWait); }, hp.sWait); }); - it("12.Change developer supplied device ID", (done) => { + + it("12- Change developer supplied device ID", (done) => { // initialize SDK initMain("ID"); Countly.change_id("changedID"); @@ -296,4 +305,12 @@ describe("Device ID type tests", () => { }, hp.sWait); }, hp.sWait); }); + + it("13- Check new DeviceIdType interface is equal to common interface", (done) => { + setTimeout(() => { + assert.equal(Countly.DeviceIdType.DEVELOPER_SUPPLIED, cc.deviceIdTypeEnums.DEVELOPER_SUPPLIED); + assert.equal(Countly.DeviceIdType.SDK_GENERATED, cc.deviceIdTypeEnums.SDK_GENERATED); + done(); + }); + }); });