diff --git a/CHANGES.txt b/CHANGES.txt index 17f636b47..5657c17ec 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,6 @@ +10.17.3 (XXX XX, 2022) + - Updated format for MySegments keys in LocalStorage, keeping backwards compatibility. + 10.17.2 (January 31, 2022) - Updated some dependencies for vulnerability fixes. - Bugfixing - Fixed internal handling of Fetch API promise rejection, to avoid unhandled exceptions on some Web browsers (issue https://github.com/splitio/javascript-client/issues/621) diff --git a/package-lock.json b/package-lock.json index b06dda9be..487c28124 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.17.2", + "version": "10.17.3-rc.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 9192a6dc9..3c7272270 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.17.2", + "version": "10.17.3-rc.4", "description": "Split SDK", "files": [ "README.md", diff --git a/src/storage/KeysLocalStorage.js b/src/storage/KeysLocalStorage.js index 1c9a044f6..1e59cec66 100644 --- a/src/storage/KeysLocalStorage.js +++ b/src/storage/KeysLocalStorage.js @@ -10,11 +10,11 @@ class KeyBuilderForLocalStorage extends KeyBuilder { } buildSegmentNameKey(segmentName) { - return `${matching(this.settings.core.key)}.${this.settings.storage.prefix}.segment.${segmentName}`; + return `${this.settings.storage.prefix}.${matching(this.settings.core.key)}.segment.${segmentName}`; } extractSegmentName(builtSegmentKeyName) { - const prefix = `${matching(this.settings.core.key)}.${this.settings.storage.prefix}.segment.`; + const prefix = `${this.settings.storage.prefix}.${matching(this.settings.core.key)}.segment.`; if (startsWith(builtSegmentKeyName, prefix)) return builtSegmentKeyName.substr(prefix.length); @@ -35,6 +35,18 @@ class KeyBuilderForLocalStorage extends KeyBuilder { buildSplitsFilterQueryKey() { return `${this.settings.storage.prefix}.splits.filterQuery`; } + + // @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3 + buildOldSegmentNameKey(segmentName) { + return `${matching(this.settings.core.key)}.${this.settings.storage.prefix}.segment.${segmentName}`; + } + // @BREAKING: The key used to start with the matching key instead of the prefix, this was changed on version 10.17.3 + extractOldSegmentKey(maybeOldKey) { + const prefix = `${matching(this.settings.core.key)}.${this.settings.storage.prefix}.segment.`; + + if (startsWith(maybeOldKey, prefix)) + return maybeOldKey.substr(prefix.length); + } } export default KeyBuilderForLocalStorage; diff --git a/src/storage/SegmentCache/InLocalStorage/index.js b/src/storage/SegmentCache/InLocalStorage/index.js index 526588dbd..82b2e60b9 100644 --- a/src/storage/SegmentCache/InLocalStorage/index.js +++ b/src/storage/SegmentCache/InLocalStorage/index.js @@ -46,9 +46,29 @@ class SegmentCacheInLocalStorage { // Scan current values from localStorage const storedSegmentNames = Object.keys(localStorage).reduce((accum, key) => { - const segmentName = this.keys.extractSegmentName(key); - - if (segmentName) accum.push(segmentName); + let segmentName = this.keys.extractSegmentName(key); + + if (segmentName) { + accum.push(segmentName); + } else { + // @BREAKING: This is only to clean up "old" keys. Remove this whole else code block. + segmentName = this.keys.extractOldSegmentKey(key); + + if (segmentName) { // this was an old segment key, let's clean up. + const newSegmentKey = this.keys.buildSegmentNameKey(segmentName); + try { + // If the new format key is not there, create it. + if (!localStorage.getItem(newSegmentKey) && segmentNames.indexOf(segmentName) > -1) { + localStorage.setItem(newSegmentKey, DEFINED); + // we are migrating a segment, let's track it. + accum.push(segmentName); + } + localStorage.removeItem(key); // we migrated the current key, let's delete it. + } catch (e) { + log.error(e); + } + } + } return accum; }, []); @@ -65,7 +85,7 @@ class SegmentCacheInLocalStorage { segmentNames.forEach(segmentName => this.addToSegment(segmentName)); } else { // Slowest path => we need to find at least 1 difference because - for(index = 0; index < segmentNames.length && storedSegmentNames.indexOf(segmentNames[index]) !== -1; index++) { + for (index = 0; index < segmentNames.length && storedSegmentNames.indexOf(segmentNames[index]) !== -1; index++) { // TODO: why empty statement? } diff --git a/src/storage/__tests__/SegmentCache/InLocalStorage/browser.spec.js b/src/storage/__tests__/SegmentCache/InLocalStorage/browser.spec.js index 9bb772504..63c61d8bc 100644 --- a/src/storage/__tests__/SegmentCache/InLocalStorage/browser.spec.js +++ b/src/storage/__tests__/SegmentCache/InLocalStorage/browser.spec.js @@ -19,5 +19,41 @@ tape('SEGMENT CACHE / in LocalStorage', assert => { assert.ok( cache.isInSegment('mocked-segment') === false ); + cache.flush(); + assert.end(); +}); + +// @BREAKING: REMOVE when removing this backwards compatibility. +tape('SEGMENT CACHE / in LocalStorage migration for mysegments keys', assert => { + + const keys = new KeyBuilder(SettingsFactory({ + core: { key: 'test_nico' }, + storage:{ prefix: 'LS_BC_test'} + })); + const cache = new SegmentCacheInLocalStorage(keys); + const oldKey1 = 'test_nico.LS_BC_test.SPLITIO.segment.segment1'; + const oldKey2 = 'test_nico.LS_BC_test.SPLITIO.segment.segment2'; + const newKey1 = keys.buildSegmentNameKey('segment1'); + const newKey2 = keys.buildSegmentNameKey('segment2'); + + cache.flush(); // cleanup before starting. + + // Not adding a full suite for LS keys now, testing here + assert.equal(oldKey1, keys.buildOldSegmentNameKey('segment1')); + assert.equal('segment1', keys.extractOldSegmentKey(oldKey1)); + + // add two segments, one we don't want to send on reset, should only be cleared, other one will be migrated. + localStorage.setItem(oldKey1, 1); + localStorage.setItem(oldKey2, 1); + assert.equal(localStorage.getItem(newKey1), null, 'control assertion'); + + cache.resetSegments(['segment1']); + + assert.equal(localStorage.getItem(newKey1), '1', 'The segment key for segment1, as is part of the new list, should be migrated.'); + assert.equal(localStorage.getItem(newKey2), null, 'The segment key for segment2 should not be migrated.'); + assert.equal(localStorage.getItem(oldKey1), null, 'Old keys are removed.'); + assert.equal(localStorage.getItem(oldKey2), null, 'Old keys are removed.'); + + cache.flush(); assert.end(); }); \ No newline at end of file diff --git a/src/utils/settings/index.js b/src/utils/settings/index.js index ea1507169..b43239cc2 100644 --- a/src/utils/settings/index.js +++ b/src/utils/settings/index.js @@ -27,7 +27,7 @@ import { API } from '../../utils/logger'; import { STANDALONE_MODE, STORAGE_MEMORY, CONSUMER_MODE, OPTIMIZED } from '../../utils/constants'; import validImpressionsMode from './impressionsMode'; -const version = '10.17.2'; +const version = '10.17.3-rc.4'; const eventsEndpointMatcher = /^\/(testImpressions|metrics|events)/; const authEndpointMatcher = /^\/v2\/auth/; const streamingEndpointMatcher = /^\/(sse|event-stream)/;