diff --git a/package-lock.json b/package-lock.json index 4fce49522..75be96c43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio", - "version": "10.28.1-rc.2", + "version": "10.28.1-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio", - "version": "10.28.1-rc.2", + "version": "10.28.1-rc.3", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.17.1-rc.1", + "@splitsoftware/splitio-commons": "1.17.1-rc.2", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", @@ -872,9 +872,9 @@ "dev": true }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.17.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.17.1-rc.1.tgz", - "integrity": "sha512-mmDcWW2iyqQF/FzLgPoRA3KXpvswk/sDIhQGWTg3WPkapnA+e4WXb+U/TSGGB/Ig88NlM76FlxMDkrHnBayDXg==", + "version": "1.17.1-rc.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.17.1-rc.2.tgz", + "integrity": "sha512-ItqKJbUvHsR5Ff6sGjJFlGCcTrQHve9eObISaHOXGPsEaMCRdke8rhQh/bo4L8W2nn5YaY4DbuXJ9w59PA1khQ==", "dependencies": { "tslib": "^2.3.1" }, @@ -8528,9 +8528,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.17.1-rc.1", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.17.1-rc.1.tgz", - "integrity": "sha512-mmDcWW2iyqQF/FzLgPoRA3KXpvswk/sDIhQGWTg3WPkapnA+e4WXb+U/TSGGB/Ig88NlM76FlxMDkrHnBayDXg==", + "version": "1.17.1-rc.2", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.17.1-rc.2.tgz", + "integrity": "sha512-ItqKJbUvHsR5Ff6sGjJFlGCcTrQHve9eObISaHOXGPsEaMCRdke8rhQh/bo4L8W2nn5YaY4DbuXJ9w59PA1khQ==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index ed707f03c..f8b6025ee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio", - "version": "10.28.1-rc.2", + "version": "10.28.1-rc.3", "description": "Split SDK", "files": [ "README.md", @@ -40,7 +40,7 @@ "node": ">=6" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.17.1-rc.1", + "@splitsoftware/splitio-commons": "1.17.1-rc.2", "@types/google.analytics": "0.0.40", "@types/ioredis": "^4.28.0", "bloom-filters": "^3.0.0", diff --git a/src/__tests__/browserSuites/ready-from-cache.spec.js b/src/__tests__/browserSuites/ready-from-cache.spec.js index b0ffd6706..c13f06bb9 100644 --- a/src/__tests__/browserSuites/ready-from-cache.spec.js +++ b/src/__tests__/browserSuites/ready-from-cache.spec.js @@ -478,6 +478,64 @@ export default function (fetchMock, assert) { }); }); + assert.test(t => { // Testing when we start with preloaded data and MEMORY storage type (is ready from cache immediately) + const testUrls = { + sdk: 'https://sdk.baseurl/readyFromCacheWithPreloadedData', + events: 'https://events.baseurl/readyFromCacheWithPreloadedData' + }; + + t.plan(5); + + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=25', { status: 200, body: { ...splitChangesMock1, since: 25 } }); + fetchMock.getOnce(testUrls.sdk + '/splitChanges?s=1.2&since=1457552620999', { status: 200, body: splitChangesMock2 }); + fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas%40split.io', { status: 200, body: membershipsNicolas }); + fetchMock.getOnce(testUrls.sdk + '/memberships/nicolas2%40split.io', { status: 200, body: { 'ms': {} } }); + + fetchMock.postOnce(testUrls.events + '/testImpressions/bulk', 200); + fetchMock.postOnce(testUrls.events + '/testImpressions/count', 200); + + const splitio = SplitFactory({ + ...baseConfig, + storage: { + type: 'MEMORY', + }, + urls: testUrls, + preloadedData: { + since: 25, + splitsData: [JSON.parse(alwaysOnSplitInverted)] + } + }); + + const client = splitio.client(); + const client2 = splitio.client('nicolas2@split.io'); + + t.equal(client.__getStatus().isReadyFromCache, true, 'Client is ready from cache'); + + t.equal(client.getTreatment('always_on'), 'off', 'It should evaluate treatments with data from cache instead of control due to Input Validation'); + t.equal(client2.getTreatment('always_on'), 'off', 'It should evaluate treatments with data from cache instead of control due to Input Validation'); + + client.on(client.Event.SDK_READY_TIMED_OUT, () => { + t.fail('It should not timeout in this scenario.'); + t.end(); + }); + + client.on(client.Event.SDK_READY_FROM_CACHE, () => { + t.fail('SDK is ready from cache immediately. SDK_READY_FROM_CACHE not emitted.'); + t.end(); + }); + + client.on(client.Event.SDK_READY, () => { + t.equal(client.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.'); + }); + client2.on(client2.Event.SDK_READY, () => { + t.equal(client2.getTreatment('always_on'), 'on', 'It should evaluate treatments with updated data after syncing with the cloud.'); + + splitio.destroy().then(() => { + t.end(); + }); + }); + }); + /** Fetch specific splits **/ assert.test(t => { // Testing when we start with cached data but without storage hash (JS SDK <=v10.24.0 and Browser SDK <=v0.12.0), and a valid split filter config diff --git a/src/__tests__/browserSuites/ready-promise.spec.js b/src/__tests__/browserSuites/ready-promise.spec.js index ebcd5843c..cb13c8107 100644 --- a/src/__tests__/browserSuites/ready-promise.spec.js +++ b/src/__tests__/browserSuites/ready-promise.spec.js @@ -561,7 +561,7 @@ export default function readyPromiseAssertions(fetchMock, assert) { }); }, 0); }); - }, fromSecondsToMillis(0.2)); + }, fromSecondsToMillis(0.25)); }, 'Validate that warning messages are properly sent'); diff --git a/src/__tests__/nodeSuites/evaluations.spec.js b/src/__tests__/nodeSuites/evaluations.spec.js index f5cf6aa53..d870d2526 100644 --- a/src/__tests__/nodeSuites/evaluations.spec.js +++ b/src/__tests__/nodeSuites/evaluations.spec.js @@ -1,4 +1,5 @@ import { SplitFactory } from '../../'; +import splitChangesMock1 from '../mocks/splitchanges.since.-1.json'; const SDK_INSTANCES_TO_TEST = 4; @@ -265,6 +266,18 @@ export default async function (config, key, assert) { getTreatmentsTests(client, i); getTreatmentsWithConfigTests(client, i); + // getState method + const serverSideState = splitio.getState(); + assert.equal(serverSideState.since, 1457552620999); + assert.equal(serverSideState.splitsData.length, splitChangesMock1.splits.length); + assert.deepEqual(serverSideState.segmentsData, { employees: [], splitters: [], developers: [] }); + assert.deepEqual(serverSideState.mySegmentsData, undefined); + const clientSideState = splitio.getState(['user1']); + assert.equal(clientSideState.since, 1457552620999); + assert.equal(clientSideState.splitsData.length, splitChangesMock1.splits.length); + assert.deepEqual(clientSideState.segmentsData, undefined); + assert.deepEqual(clientSideState.mySegmentsData, { user1: [] }); + await client.destroy(); tested++; diff --git a/src/__tests__/offline/browser.spec.js b/src/__tests__/offline/browser.spec.js index bce2020eb..5c99786c0 100644 --- a/src/__tests__/offline/browser.spec.js +++ b/src/__tests__/offline/browser.spec.js @@ -112,8 +112,6 @@ tape('Browser offline mode', function (assert) { }); const sdkReadyFromCache = (client) => () => { - assert.equal(factory.settings.storage.type, 'MEMORY', 'In localhost mode, storage must fallback to memory storage'); - const clientStatus = client.__getStatus(); assert.equal(clientStatus.isReadyFromCache, true, 'If ready from cache, READY_FROM_CACHE status must be true'); assert.equal(clientStatus.isReady, false, 'READY status must not be set before READY_FROM_CACHE'); diff --git a/src/factory/browser.js b/src/factory/browser.js index 9b877a36d..e9793f056 100644 --- a/src/factory/browser.js +++ b/src/factory/browser.js @@ -8,7 +8,6 @@ import { sdkManagerFactory } from '@splitsoftware/splitio-commons/src/sdkManager import { sdkClientMethodCSFactory } from '@splitsoftware/splitio-commons/src/sdkClient/sdkClientMethodCSWithTT'; import { impressionObserverCSFactory } from '@splitsoftware/splitio-commons/src/trackers/impressionObserver/impressionObserverCS'; import { integrationsManagerFactory } from '@splitsoftware/splitio-commons/src/integrations/browser'; -import { __InLocalStorageMockFactory } from '@splitsoftware/splitio-commons/src/utils/settingsValidation/storage/storageCS'; import { sdkFactory } from '@splitsoftware/splitio-commons/src/sdkFactory'; import { LOCALHOST_MODE, STORAGE_LOCALSTORAGE } from '@splitsoftware/splitio-commons/src/utils/constants'; import { createUserConsentAPI } from '@splitsoftware/splitio-commons/src/consent/sdkUserConsent'; @@ -20,10 +19,8 @@ const syncManagerOnlineCSFactory = syncManagerOnlineFactory(pollingManagerCSFact function getStorage(settings) { return settings.storage.type === STORAGE_LOCALSTORAGE ? - InLocalStorage(settings.storage) - : settings.storage.__originalType === STORAGE_LOCALSTORAGE ? - __InLocalStorageMockFactory - : InMemoryStorageCSFactory; + InLocalStorage(settings.storage) : + InMemoryStorageCSFactory; } /** diff --git a/src/factory/node.js b/src/factory/node.js index d8141037a..07ecd973d 100644 --- a/src/factory/node.js +++ b/src/factory/node.js @@ -4,6 +4,7 @@ import { pushManagerFactory } from '@splitsoftware/splitio-commons/src/sync/stre import { pollingManagerSSFactory } from '@splitsoftware/splitio-commons/src/sync/polling/pollingManagerSS'; import { InRedisStorage } from '@splitsoftware/splitio-commons/src/storages/inRedis'; import { InMemoryStorageFactory } from '@splitsoftware/splitio-commons/src/storages/inMemory/InMemoryStorage'; +import { getSnapshot } from '@splitsoftware/splitio-commons/src/storages/dataLoader'; import { sdkManagerFactory } from '@splitsoftware/splitio-commons/src/sdkManager'; import { sdkClientMethodFactory } from '@splitsoftware/splitio-commons/src/sdkClient/sdkClientMethod'; import { impressionObserverSSFactory } from '@splitsoftware/splitio-commons/src/trackers/impressionObserver/impressionObserverSS'; @@ -47,7 +48,17 @@ function getModules(settings) { impressionsObserverFactory: impressionObserverSSFactory, - filterAdapterFactory: bloomFilterFactory + filterAdapterFactory: bloomFilterFactory, + + extraProps: (params) => { + if (params.settings.mode !== CONSUMER_MODE) { + return { + getState(userKeys) { + return getSnapshot(params.storage, userKeys); + } + }; + } + } }; switch (settings.mode) { diff --git a/src/settings/defaults/version.js b/src/settings/defaults/version.js index 1e30feddf..470a9c327 100644 --- a/src/settings/defaults/version.js +++ b/src/settings/defaults/version.js @@ -1 +1 @@ -export const packageVersion = '10.28.1-rc.2'; +export const packageVersion = '10.28.1-rc.3'; diff --git a/src/settings/storage/browser.js b/src/settings/storage/browser.js index 10a9e3eea..bcd644cce 100644 --- a/src/settings/storage/browser.js +++ b/src/settings/storage/browser.js @@ -1,36 +1,23 @@ import { isLocalStorageAvailable } from '@splitsoftware/splitio-commons/src/utils/env/isLocalStorageAvailable'; -import { LOCALHOST_MODE, STORAGE_MEMORY } from '@splitsoftware/splitio-commons/src/utils/constants'; +import { STORAGE_MEMORY } from '@splitsoftware/splitio-commons/src/utils/constants'; const STORAGE_LOCALSTORAGE = 'LOCALSTORAGE'; export function validateStorage(settings) { let { log, - mode, storage: { type, options = {}, prefix } = { type: STORAGE_MEMORY }, } = settings; - let __originalType; - - const fallbackToMemory = () => { - __originalType = type; - type = STORAGE_MEMORY; - }; - - // In localhost mode, fallback to Memory storage and track original type to emit SDK_READY_FROM_CACHE if corresponds. - // ATM, other mode settings (e.g., 'consumer') are ignored in client-side API, and so treated as standalone. - if (mode === LOCALHOST_MODE && type === STORAGE_LOCALSTORAGE) { - fallbackToMemory(); - } // If an invalid storage type is provided OR we want to use LOCALSTORAGE and // it's not available, fallback into MEMORY if (type !== STORAGE_MEMORY && type !== STORAGE_LOCALSTORAGE || type === STORAGE_LOCALSTORAGE && !isLocalStorageAvailable()) { - fallbackToMemory(); + type = STORAGE_MEMORY; log.error('Invalid or unavailable storage. Fallback into MEMORY storage'); } @@ -38,6 +25,5 @@ export function validateStorage(settings) { type, options, prefix, - __originalType }; } diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 27a1dcade..9229cba3e 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -94,6 +94,7 @@ interface ISettings { options: Object, type: StorageType }, + readonly preloadedData?: SplitIO.PreloadedData, readonly urls: { events: string, sdk: string, @@ -953,6 +954,10 @@ declare namespace SplitIO { * @typedef {string} ConsentStatus */ type ConsentStatus = 'GRANTED' | 'DECLINED' | 'UNKNOWN'; + /** + * Defines the format of rollout plan data to preload on the factory storage (cache). + */ + type PreloadedData = Object; /** * Settings interface for SDK instances created on the browser * @interface IBrowserSettings @@ -1385,7 +1390,11 @@ declare namespace SplitIO { * @function manager * @returns {IManager} The manager instance. */ - manager(): IManager + manager(): IManager, + /** + * @TODO add description + */ + getState(): PreloadedData, } /** * This represents the interface for the SDK instance with synchronous storage.