diff --git a/CHANGELOG.md b/CHANGELOG.md index 967da13b..e2bc6dce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## X.X.X +- You can now provide custom storage methods to the SDK + ## 23.6.3 - You can now add segmentation while presenting a widget with 'present_feedback_widget' diff --git a/examples/worker.js b/examples/worker.js index 1d72ba9d..402f2f75 100644 --- a/examples/worker.js +++ b/examples/worker.js @@ -1,9 +1,22 @@ importScripts("../lib/countly.js"); +const STORE={}; // in-memory storage for worker + Countly.init({ app_key: "YOUR_APP_KEY", url: "https://try.count.ly", - debug: true + debug: true, + storage: { + getItem: function (key) { + return STORE[key]; + }, + setItem: function (key, value) { + STORE[key] = value; + }, + removeItem: function (key) { + delete STORE[key]; + } + } }); onmessage = function (e) { diff --git a/lib/countly.js b/lib/countly.js index ab88c4e6..d2b1dc82 100644 --- a/lib/countly.js +++ b/lib/countly.js @@ -292,7 +292,7 @@ this.ignore_visitor = getConfig("ignore_visitor", ob, false); this.require_consent = getConfig("require_consent", ob, false); this.track_domains = !isBrowser ? undefined : getConfig("track_domains", ob, true); - this.storage = !isBrowser ? "none" : getConfig("storage", ob, "default"); + this.storage = getConfig("storage", ob, "default"); this.enableOrientationTracking = !isBrowser ? undefined : getConfig("enable_orientation_tracking", ob, true); this.maxKeyLength = getConfig("max_key_length", ob, configurationDefaultValues.MAX_KEY_LENGTH); this.maxValueSize = getConfig("max_value_size", ob, configurationDefaultValues.MAX_VALUE_SIZE); @@ -4419,9 +4419,9 @@ * @returns {Varies} values stored for key */ function getValueFromStorage(key, useLocalStorage, useRawKey) { - // check if we should use storage at all - if (self.storage === "none") { - log(logLevelEnums.WARNING, "Storage is disabled. Value with key: " + key + " won't be retrieved"); + // check if we should use storage at all. If in worker context but no storage is available, return early + if (self.storage === "none" || (typeof self.storage !== "object" && !isBrowser)) { + log(logLevelEnums.DEBUG, "Storage is disabled. Value with key: [" + key + "] won't be retrieved"); return; } @@ -4433,11 +4433,17 @@ } } + var data; + // use dev provided storage if available + if (typeof self.storage === "object" && typeof self.storage.getItem === "function") { + data = self.storage.getItem(key); + return key.endsWith("cly_id") ? data : self.deserialize(data); + } + // developer set values takes priority if (useLocalStorage === undefined) { useLocalStorage = lsSupport; } - var data; // Get value if (useLocalStorage) { // Native support @@ -4465,8 +4471,8 @@ */ function setValueInStorage(key, value, useLocalStorage, useRawKey) { // check if we should use storage options at all - if (self.storage === "none") { - log(logLevelEnums.WARNING, "Storage is disabled. Value with key: " + key + " won't be stored"); + if (self.storage === "none" || (typeof self.storage !== "object" && !isBrowser)) { + log(logLevelEnums.DEBUG, "Storage is disabled. Value with key: " + key + " won't be stored"); return; } @@ -4478,12 +4484,18 @@ } } - // developer set values takes priority - if (useLocalStorage === undefined) { - useLocalStorage = lsSupport; - } - if (typeof value !== "undefined" && value !== null) { + // use dev provided storage if available + if (typeof self.storage === "object" && typeof self.storage.setItem === "function") { + self.storage.setItem(key, value); + return; + } + + // developer set values takes priority + if (useLocalStorage === undefined) { + useLocalStorage = lsSupport; + } + value = self.serialize(value); // Set the store if (useLocalStorage) { // Native support @@ -4504,8 +4516,8 @@ */ function removeValueFromStorage(key, useLocalStorage, useRawKey) { // check if we should use storage options at all - if (self.storage === "none") { - log(logLevelEnums.WARNING, "Storage is disabled. Value with key: " + key + " won't be removed"); + if (self.storage === "none" || (typeof self.storage !== "object" && !isBrowser)) { + log(logLevelEnums.DEBUG, "Storage is disabled. Value with key: " + key + " won't be removed"); return; } @@ -4517,6 +4529,12 @@ } } + // use dev provided storage if available + if (typeof self.storage === "object" && typeof self.storage.removeItem === "function") { + self.storage.removeItem(key); + return; + } + // developer set values takes priority if (useLocalStorage === undefined) { useLocalStorage = lsSupport; @@ -4837,7 +4855,11 @@ * @param {string} conf.metrics._browser_version - browser version * @param {string} conf.metrics._ua - user agent string * @param {Object=} [conf.headers={}] - Object to override or add headers to all SDK requests - * @param {string=} [conf.storage=default] - What type of storage to use, by default uses local storage and would fallback to cookies, but you can set values "localstorage" or "cookies" to force only specific storage, or use "none" to not use any storage and keep everything in memory + * @param {string | Object} [conf.storage=default] - What type of storage to use, by default uses local storage and would fallback to cookies, but you can set values "localstorage" or "cookies" to force only specific storage, or use "none" to not use any storage and keep everything in memory + * If developer wants to provide their own storage methods they can provide an object with methods getItem(key:string), setItem(key:string, value:string), removeItem(key:string) + * SDK would hand the key and value params to use these methods to store and retrieve data. + * Here key is the key of the data to be stored and value is the value of the data to be stored. + * This can be used in scenarios where a clear storage method is not available, like in web workers * @returns {Object} countly tracker instance * @example * Countly.init({