From f82e255e808e8c64d5802925ab9b2aae90422371 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 19 Sep 2022 17:40:07 -0300 Subject: [PATCH 1/9] upgrade js-commons and tests to support NONE impressions mode --- src/__tests__/browserSuites/impressions.spec.js | 2 +- src/__tests__/browserSuites/telemetry.spec.js | 6 ++++-- src/__tests__/browserSuites/use-beacon-api.spec.js | 6 ++++-- src/__tests__/browserSuites/user-consent.spec.js | 3 ++- src/__tests__/consumer/browser_consumer.spec.js | 7 +++++-- src/__tests__/consumer/browser_consumer_partial.spec.js | 2 +- src/__tests__/testUtils/index.js | 2 +- src/platform/getModules.ts | 3 +-- types/splitio.d.ts | 2 +- 9 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/__tests__/browserSuites/impressions.spec.js b/src/__tests__/browserSuites/impressions.spec.js index 8e73c5d..6be91e6 100644 --- a/src/__tests__/browserSuites/impressions.spec.js +++ b/src/__tests__/browserSuites/impressions.spec.js @@ -94,7 +94,7 @@ export default function (fetchMock, assert) { fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => { const data = JSON.parse(opts.body); - assert.equal(data.pf.length, 2, 'We should generated 2 impressions count.'); + assert.equal(data.pf.length, 1, 'We should generate impressions count for one feature.'); // finding these validate the feature names collection too const dependencyChildImpr = data.pf.filter(e => e.f === 'hierarchical_splits_test')[0]; diff --git a/src/__tests__/browserSuites/telemetry.spec.js b/src/__tests__/browserSuites/telemetry.spec.js index e6ebbad..80f1ac1 100644 --- a/src/__tests__/browserSuites/telemetry.spec.js +++ b/src/__tests__/browserSuites/telemetry.spec.js @@ -69,8 +69,9 @@ export default async function telemetryBrowserSuite(fetchMock, assert) { assert.equal(getLatencyCount(data.mL.tr), 1, 'One latency metric for track'); delete data.hL; delete data.mL; + // @TODO check if iDe value is correct assert.deepEqual(data, { - mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 3, iDr: 0, spC: 31, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [] + mE: {}, hE: { sp: { 500: 1 }, ms: { 500: 1 } }, tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 31, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [] }, 'metrics/usage JSON payload should be the expected'); finish.next(); @@ -87,9 +88,10 @@ export default async function telemetryBrowserSuite(fetchMock, assert) { assert.true(data.sL > 0, 'sessionLengthMs must be defined'); delete data.sL; + // @TODO check if iDe value is correct assert.deepEqual(data, { mL: {}, mE: {}, hE: {}, hL: {}, // errors and latencies were popped - tR: 0, aR: 0, iQ: 4, iDe: 3, iDr: 0, spC: 31, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [] + tR: 0, aR: 0, iQ: 4, iDe: 1, iDr: 0, spC: 31, seC: 1, skC: 1, eQ: 1, eD: 0, sE: [], t: [] }, '2nd metrics/usage JSON payload should be the expected'); return 200; }); diff --git a/src/__tests__/browserSuites/use-beacon-api.spec.js b/src/__tests__/browserSuites/use-beacon-api.spec.js index 8cb2122..41ab094 100644 --- a/src/__tests__/browserSuites/use-beacon-api.spec.js +++ b/src/__tests__/browserSuites/use-beacon-api.spec.js @@ -110,7 +110,8 @@ function beaconApiSendTest(fetchMock, assert) { const splitio = SplitFactory(config); const client = splitio.client(); client.on(client.Event.SDK_READY, () => { - client.getTreatment('hierarchical_splits_test'); + client.getTreatment('hierarchical_splits_test'); // first impression counted in backend + client.getTreatment('hierarchical_splits_test'); // impression counted in sdk client.track('sometraffictype', 'someEvent', 10); // trigger both events inmmediatly, before scheduled push of events and impressions, to assert that beacon requests are not duplicated @@ -174,7 +175,8 @@ function fallbackTest(fetchMock, assert) { }); client.on(client.Event.SDK_READY, () => { - client.getTreatment('hierarchical_splits_test'); + client.getTreatment('hierarchical_splits_test');// first impression counted in backend + client.getTreatment('hierarchical_splits_test');// impression counted in sdk client.track('sometraffictype', 'someEvent', 10); // trigger both events inmmediatly, before scheduled push of events and impressions, to assert that POST requests are not duplicated triggerPagehideEvent(); diff --git a/src/__tests__/browserSuites/user-consent.spec.js b/src/__tests__/browserSuites/user-consent.spec.js index 7dd3cad..024fcb4 100644 --- a/src/__tests__/browserSuites/user-consent.spec.js +++ b/src/__tests__/browserSuites/user-consent.spec.js @@ -158,7 +158,8 @@ export default function userConsent(fetchMock, t) { await client.ready(); assert.equal(client.track('user', 'event1'), true, 'Events queue is full, but submitter is not executed'); - assert.equal(client.getTreatment('always_on'), 'on', 'Impressions queue is full, but submitter is not executed'); + assert.equal(client.getTreatment('always_on'), 'on', 'Impressions queue is full, but submitter is not executed');// First impression counted in backend + assert.equal(client.getTreatment('always_on'), 'on', 'Impressions queue is full, but submitter is not executed');// impression counted in sdk let submitterCalls = 0; const start = Date.now(); diff --git a/src/__tests__/consumer/browser_consumer.spec.js b/src/__tests__/consumer/browser_consumer.spec.js index b3f206b..3de89e2 100644 --- a/src/__tests__/consumer/browser_consumer.spec.js +++ b/src/__tests__/consumer/browser_consumer.spec.js @@ -26,12 +26,15 @@ const config = { storage: PluggableStorage({ prefix: wrapperPrefix, wrapper: wrapperInstance - }) + }), + sync: { + impressionsMode: 'DEBUG' + }, }; tape('Browser Consumer mode with pluggable storage', function (t) { - t.test('Regular usage', async (assert) => { + t.test('Regular usage - DEBUG strategy', async (assert) => { // Load wrapper with data to do the proper tests await applyOperations(wrapperInstance); diff --git a/src/__tests__/consumer/browser_consumer_partial.spec.js b/src/__tests__/consumer/browser_consumer_partial.spec.js index 4f493c7..81476ff 100644 --- a/src/__tests__/consumer/browser_consumer_partial.spec.js +++ b/src/__tests__/consumer/browser_consumer_partial.spec.js @@ -44,7 +44,7 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) { * We only validates regular usage. * Corner cases, such as wrapper connection timeout and operation errors, are validated in `browser_consumer.spec.js` */ - t.test('Regular usage', async (assert) => { + t.test('Regular usage - OPTIMIZED strategy', async (assert) => { fetchMock.postOnce(url(config, '/testImpressions/bulk'), (url, req) => { assert.equal(req.headers.SplitSDKImpressionsMode, OPTIMIZED, 'Impressions mode is OPTIMIZED by default'); diff --git a/src/__tests__/testUtils/index.js b/src/__tests__/testUtils/index.js index 302e944..2717982 100644 --- a/src/__tests__/testUtils/index.js +++ b/src/__tests__/testUtils/index.js @@ -43,7 +43,7 @@ export function hasNoCacheHeader(fetchMockOpts) { return fetchMockOpts.headers['Cache-Control'] === 'no-cache'; } -const telemetryEndpointMatcher = /^\/v1\/metrics\/(config|usage)/; +const telemetryEndpointMatcher = /^\/v1\/(metrics|keys)\/(config|usage|ss|cs)/; const eventsEndpointMatcher = /^\/(testImpressions|metrics|events)/; const authEndpointMatcher = /^\/v2\/auth/; const streamingEndpointMatcher = /^\/(sse|event-stream)/; diff --git a/src/platform/getModules.ts b/src/platform/getModules.ts index 8d5416f..90c45e9 100644 --- a/src/platform/getModules.ts +++ b/src/platform/getModules.ts @@ -8,7 +8,6 @@ import { BrowserSignalListener } from '@splitsoftware/splitio-commons/src/listen import { impressionObserverCSFactory } from '@splitsoftware/splitio-commons/src/trackers/impressionObserver/impressionObserverCS'; import { pluggableIntegrationsManagerFactory } from '@splitsoftware/splitio-commons/src/integrations/pluggable'; -import { shouldAddPt } from '@splitsoftware/splitio-commons/src/trackers/impressionObserver/utils'; import { IPlatform, ISdkFactoryParams } from '@splitsoftware/splitio-commons/src/sdkFactory/types'; import { ISettings } from '@splitsoftware/splitio-commons/src/types'; import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE, LOCALHOST_MODE } from '@splitsoftware/splitio-commons/src/utils/constants'; @@ -40,7 +39,7 @@ export function getModules(settings: ISettings, platform: IPlatform): ISdkFactor integrationsManagerFactory: settings.integrations && settings.integrations.length > 0 ? pluggableIntegrationsManagerFactory.bind(null, settings.integrations) : undefined, - impressionsObserverFactory: shouldAddPt(settings) ? impressionObserverCSFactory : undefined, + impressionsObserverFactory: impressionObserverCSFactory, extraProps: (params) => { return { diff --git a/types/splitio.d.ts b/types/splitio.d.ts index d54f534..9c1a89f 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -809,7 +809,7 @@ declare namespace SplitIO { * ImpressionsMode type * @typedef {string} ImpressionsMode */ - type ImpressionsMode = 'OPTIMIZED' | 'DEBUG'; + type ImpressionsMode = 'OPTIMIZED' | 'DEBUG' | 'NONE'; /** * User consent status. * @typedef {string} ConsentStatus From 004409148dc905f310c7c746f082d47e4010ab39 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 19 Sep 2022 19:31:43 -0300 Subject: [PATCH 2/9] updated e2e tests for consumer mode with pluggable storage --- .../consumer/browser_consumer.spec.js | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) diff --git a/src/__tests__/consumer/browser_consumer.spec.js b/src/__tests__/consumer/browser_consumer.spec.js index 3de89e2..a601fb1 100644 --- a/src/__tests__/consumer/browser_consumer.spec.js +++ b/src/__tests__/consumer/browser_consumer.spec.js @@ -2,6 +2,7 @@ import tape from 'tape-catch'; import sinon from 'sinon'; import { inMemoryWrapperFactory } from '@splitsoftware/splitio-commons/src/storages/pluggable/inMemoryWrapper'; import { SDK_NOT_READY, EXCEPTION } from '@splitsoftware/splitio-commons/src/utils/labels'; +import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time'; import { applyOperations } from './wrapper-commands'; import { nearlyEqual } from '../testUtils'; import { version } from '../../../package.json'; @@ -15,6 +16,8 @@ const wrapperPrefix = 'PLUGGABLE_STORAGE_UT'; const wrapperInstance = inMemoryWrapperFactory(); const TOTAL_RAW_IMPRESSIONS = 18; const TOTAL_EVENTS = 5; +const DEDUPED_IMPRESSIONS = 2; +const timeFrame = Date.now(); /** @type SplitIO.IBrowserAsyncSettings */ const config = { @@ -177,6 +180,292 @@ tape('Browser Consumer mode with pluggable storage', function (t) { }); }); + t.test('Regular usage - OPTIMIZED strategy', async (assert) => { + const wrapperInstance = inMemoryWrapperFactory(); + // Load wrapper with data to do the proper tests + await applyOperations(wrapperInstance); + + /** @type SplitIO.ImpressionData[] */ + const impressions = []; + const disconnectSpy = sinon.spy(wrapperInstance, 'disconnect'); + + // Overwrite Math.random to track telemetry + const originalMathRandom = Math.random; Math.random = () => 0.5; + const sdk = SplitFactory({ + ...config, + storage: PluggableStorage({ + prefix: wrapperPrefix, + wrapper: wrapperInstance + }), + sync: { + impressionsMode: 'OPTIMIZED' + }, + impressionListener: { + logImpression(data) { impressions.push(data); } + } + }); + Math.random = originalMathRandom; // restore + + const client = sdk.client(); + const otherClient = sdk.client('emi@split.io'); + + /** Evaluation and track methods before SDK_READY */ + + const getTreatmentResult = client.getTreatment('UT_IN_SEGMENT'); + + assert.equal(typeof getTreatmentResult.then, 'function', 'GetTreatment calls should always return a promise on Consumer mode.'); + assert.equal(await getTreatmentResult, 'control', 'Evaluations using pluggable storage should be control if initiated before SDK_READY.'); + assert.equal(client.__getStatus().isReadyFromCache, false, 'SDK in consumer mode is not operational inmediatelly'); + assert.equal(client.__getStatus().isReady, false, 'SDK in consumer mode is not operational inmediatelly'); + + const trackResult = otherClient.track('user', 'test.event', 18); + assert.equal(typeof trackResult.then, 'function', 'Track calls should always return a promise on Consumer mode.'); + assert.true(await trackResult, 'If the wrapper operation success to queue the event, the promise will resolve to true'); + + /** Evaluation and track methods on SDK_READY with OPTIMIZED impressions mode */ + + await client.ready(); + await otherClient.ready(); // waiting to avoid 'control' with label 'sdk not ready' + + assert.equal(await client.getTreatment('UT_IN_SEGMENT'), 'on', '`getTreatment` evaluation using pluggable storage should be correct.'); + assert.equal((await otherClient.getTreatmentWithConfig('UT_IN_SEGMENT')).treatment, 'off', '`getTreatmentWithConfig` evaluation using pluggable storage should be correct.'); + + assert.equal((await client.getTreatments(['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'], 'off', '`getTreatments` evaluation using pluggable storage should be correct.'); + assert.equal((await otherClient.getTreatmentsWithConfig(['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'].treatment, 'on', '`getTreatmentsWithConfig` evaluation using pluggable storage should be correct.'); + + client.setAttribute('permissions', ['not_matching']); + assert.equal(await client.getTreatment('UT_SET_MATCHER', { + permissions: ['admin'] + }), 'on', 'Evaluations using pluggable storage should be correct.'); + client.setAttributes({ permissions: ['admin'] }); + assert.equal(await client.getTreatment('UT_SET_MATCHER', { + permissions: ['not_matching'] + }), 'off', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('UT_SET_MATCHER'), 'on', 'Evaluations using pluggable storage and attributes binding should be correct.'); + + client.setAttribute('permissions', ['not_matching']); + assert.equal(await client.getTreatment('UT_NOT_SET_MATCHER', { + permissions: ['create'] + }), 'off', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('UT_NOT_SET_MATCHER'), 'on', 'Evaluations using pluggable storage should be correct.'); + assert.deepEqual(await client.getTreatmentWithConfig('UT_NOT_SET_MATCHER'), { + treatment: 'on', + config: null + }, 'Evaluations using pluggable storage should be correct, including configs.'); + client.clearAttributes(); + assert.deepEqual(await client.getTreatmentWithConfig('always-o.n-with-config'), { + treatment: 'o.n', + config: '{"color":"brown"}' + }, 'Evaluations using pluggable storage should be correct, including configs.'); + + assert.equal(await client.getTreatment('always-on'), 'on', 'Evaluations using pluggable storage should be correct.'); + + // Below splits were added manually. + // They are all_keys (always evaluate to on) which depend from always-on split. the _on/off is what treatment they are expecting there. + assert.equal(await client.getTreatment('hierarchical_splits_testing_on'), 'on', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('hierarchical_splits_testing_off'), 'off', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using pluggable storage should be correct.'); + + assert.equal(typeof client.track('user', 'test.event', 18).then, 'function', 'Track calls should always return a promise on Consumer mode.'); + assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Consumer mode, even when parameters are incorrect.'); + + assert.true(await client.track('user', 'test.event', 18), 'If the event was succesfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + + // New shared client created + const newClient = sdk.client('other'); + newClient.track('user', 'test.event', 18).then(result => assert.true(result, 'Track should attempt to track, whether SDK_READY has been emitted or not.')); + newClient.getTreatment('UT_IN_SEGMENT').then(result => assert.equal(result, 'control', '`getTreatment` evaluation is control if shared client is not ready yet.')); + + await newClient.ready(); + assert.true(await newClient.track('user', 'test.event', 18), 'Track should attempt to track, whether SDK_READY has been emitted or not.'); + assert.equal((await newClient.getTreatment('UT_IN_SEGMENT')), 'off', '`getTreatment` evaluation using pluggable storage should be correct.'); + + await client.ready(); // promise already resolved + await newClient.destroy(); + await otherClient.destroy(); + await client.destroy(); + + assert.equal(disconnectSpy.callCount, 1, 'Wrapper disconnect method should be called only once, when the main client is destroyed'); + + // Validate stored impressions and events + const trackedImpressions = await wrapperInstance.popItems('PLUGGABLE_STORAGE_UT.SPLITIO.impressions', await wrapperInstance.getItemsCount('PLUGGABLE_STORAGE_UT.SPLITIO.impressions')); + const trackedEvents = await wrapperInstance.popItems('PLUGGABLE_STORAGE_UT.SPLITIO.events', await wrapperInstance.getItemsCount('PLUGGABLE_STORAGE_UT.SPLITIO.events')); + assert.equal(trackedImpressions.length, TOTAL_RAW_IMPRESSIONS - DEDUPED_IMPRESSIONS, 'Tracked impressions should be present in the external storage'); + assert.equal(trackedEvents.length, TOTAL_EVENTS, 'Tracked events should be present in the external storage'); + + // Validate impression counts + const trackedImpressionCounts = await wrapperInstance.getKeysByPrefix('PLUGGABLE_STORAGE_UT.SPLITIO.impressions.count') + .then(impressionCountsKeys => Promise.all(impressionCountsKeys.map(key => wrapperInstance.get(key).then(count => ([key, count]))))); + const expectedImpressionCount = [ + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_NOT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '1'], + ] + assert.deepEqual(trackedImpressionCounts, expectedImpressionCount, 'Impression counts should be present in the external storage'); + + // Assert impressionsListener + setTimeout(() => { + assert.equal(impressions.length, TOTAL_RAW_IMPRESSIONS, 'Each evaluation has its corresponting impression'); + assert.equal(impressions[0].impression.label, SDK_NOT_READY, 'The first impression is control with label "sdk not ready"'); + + assert.end(); + }); + }); + + t.test('Regular usage - NONE strategy', async (assert) => { + const wrapperInstance = inMemoryWrapperFactory(); + // Load wrapper with data to do the proper tests + await applyOperations(wrapperInstance); + + /** @type SplitIO.ImpressionData[] */ + const impressions = []; + const disconnectSpy = sinon.spy(wrapperInstance, 'disconnect'); + + // Overwrite Math.random to not track telemetry + const originalMathRandom = Math.random; Math.random = () => 0.5; + const sdk = SplitFactory({ + ...config, + storage: PluggableStorage({ + prefix: wrapperPrefix, + wrapper: wrapperInstance + }), + sync: { + impressionsMode: 'NONE' + }, + impressionListener: { + logImpression(data) { impressions.push(data); } + } + }); + Math.random = originalMathRandom; // restore + + const client = sdk.client(); + const otherClient = sdk.client('emi@split.io'); + + /** Evaluation and track methods before SDK_READY */ + + const getTreatmentResult = client.getTreatment('UT_IN_SEGMENT'); + + assert.equal(typeof getTreatmentResult.then, 'function', 'GetTreatment calls should always return a promise on Consumer mode.'); + assert.equal(await getTreatmentResult, 'control', 'Evaluations using pluggable storage should be control if initiated before SDK_READY.'); + assert.equal(client.__getStatus().isReadyFromCache, false, 'SDK in consumer mode is not operational inmediatelly'); + assert.equal(client.__getStatus().isReady, false, 'SDK in consumer mode is not operational inmediatelly'); + + const trackResult = otherClient.track('user', 'test.event', 18); + assert.equal(typeof trackResult.then, 'function', 'Track calls should always return a promise on Consumer mode.'); + assert.true(await trackResult, 'If the wrapper operation success to queue the event, the promise will resolve to true'); + + /** Evaluation and track methods on SDK_READY with OPTIMIZED impressions mode */ + + await client.ready(); + await otherClient.ready(); // waiting to avoid 'control' with label 'sdk not ready' + + assert.equal(await client.getTreatment('UT_IN_SEGMENT'), 'on', '`getTreatment` evaluation using pluggable storage should be correct.'); + assert.equal((await otherClient.getTreatmentWithConfig('UT_IN_SEGMENT')).treatment, 'off', '`getTreatmentWithConfig` evaluation using pluggable storage should be correct.'); + + assert.equal((await client.getTreatments(['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'], 'off', '`getTreatments` evaluation using pluggable storage should be correct.'); + assert.equal((await otherClient.getTreatmentsWithConfig(['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'].treatment, 'on', '`getTreatmentsWithConfig` evaluation using pluggable storage should be correct.'); + + client.setAttribute('permissions', ['not_matching']); + assert.equal(await client.getTreatment('UT_SET_MATCHER', { + permissions: ['admin'] + }), 'on', 'Evaluations using pluggable storage should be correct.'); + client.setAttributes({ permissions: ['admin'] }); + assert.equal(await client.getTreatment('UT_SET_MATCHER', { + permissions: ['not_matching'] + }), 'off', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('UT_SET_MATCHER'), 'on', 'Evaluations using pluggable storage and attributes binding should be correct.'); + + client.setAttribute('permissions', ['not_matching']); + assert.equal(await client.getTreatment('UT_NOT_SET_MATCHER', { + permissions: ['create'] + }), 'off', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('UT_NOT_SET_MATCHER'), 'on', 'Evaluations using pluggable storage should be correct.'); + assert.deepEqual(await client.getTreatmentWithConfig('UT_NOT_SET_MATCHER'), { + treatment: 'on', + config: null + }, 'Evaluations using pluggable storage should be correct, including configs.'); + client.clearAttributes(); + assert.deepEqual(await client.getTreatmentWithConfig('always-o.n-with-config'), { + treatment: 'o.n', + config: '{"color":"brown"}' + }, 'Evaluations using pluggable storage should be correct, including configs.'); + + assert.equal(await client.getTreatment('always-on'), 'on', 'Evaluations using pluggable storage should be correct.'); + + // Below splits were added manually. + // They are all_keys (always evaluate to on) which depend from always-on split. the _on/off is what treatment they are expecting there. + assert.equal(await client.getTreatment('hierarchical_splits_testing_on'), 'on', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('hierarchical_splits_testing_off'), 'off', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using pluggable storage should be correct.'); + + assert.equal(typeof client.track('user', 'test.event', 18).then, 'function', 'Track calls should always return a promise on Consumer mode.'); + assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Consumer mode, even when parameters are incorrect.'); + + assert.true(await client.track('user', 'test.event', 18), 'If the event was succesfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + + // New shared client created + const newClient = sdk.client('other'); + newClient.track('user', 'test.event', 18).then(result => assert.true(result, 'Track should attempt to track, whether SDK_READY has been emitted or not.')); + newClient.getTreatment('UT_IN_SEGMENT').then(result => assert.equal(result, 'control', '`getTreatment` evaluation is control if shared client is not ready yet.')); + + await newClient.ready(); + assert.true(await newClient.track('user', 'test.event', 18), 'Track should attempt to track, whether SDK_READY has been emitted or not.'); + assert.equal((await newClient.getTreatment('UT_IN_SEGMENT')), 'off', '`getTreatment` evaluation using pluggable storage should be correct.'); + + await client.ready(); // promise already resolved + await newClient.destroy(); + await otherClient.destroy(); + await client.destroy(); + + assert.equal(disconnectSpy.callCount, 1, 'Wrapper disconnect method should be called only once, when the main client is destroyed'); + + // Validate stored events and no impressions + const trackedImpressions = await wrapperInstance.popItems('PLUGGABLE_STORAGE_UT.SPLITIO.impressions', await wrapperInstance.getItemsCount('PLUGGABLE_STORAGE_UT.SPLITIO.impressions')); + const trackedEvents = await wrapperInstance.popItems('PLUGGABLE_STORAGE_UT.SPLITIO.events', await wrapperInstance.getItemsCount('PLUGGABLE_STORAGE_UT.SPLITIO.events')); + assert.equal(trackedImpressions.length, 0, 'No impressions are tracked in NONE impressions mode'); + assert.equal(trackedEvents.length, TOTAL_EVENTS, 'Tracked events should be present in the external storage'); + + // Validate impression counts + const trackedImpressionCounts = await wrapperInstance.getKeysByPrefix('PLUGGABLE_STORAGE_UT.SPLITIO.impressions.count') + .then(impressionCountsKeys => Promise.all(impressionCountsKeys.map(key => wrapperInstance.get(key).then(count => ([key, count]))))); + const expectedImpressionCount = [ + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_IN_SEGMENT::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_NOT_IN_SEGMENT::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_NOT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::always-o.n-with-config::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::always-on::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::hierarchical_splits_testing_on::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::hierarchical_splits_testing_off::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::hierarchical_splits_testing_on_negated::${truncateTimeFrame(timeFrame)}`, '1'] + ]; + assert.deepEqual(trackedImpressionCounts, expectedImpressionCount, 'Impression counts should be present in the external storage'); + + // Validate unique keys + const trackedUniqueKeys = await wrapperInstance.popItems('PLUGGABLE_STORAGE_UT.SPLITIO.uniquekeys', await wrapperInstance.getItemsCount('PLUGGABLE_STORAGE_UT.SPLITIO.uniquekeys')); + const expectedUniqueKeys = [ + JSON.stringify({ 'f': 'UT_IN_SEGMENT', 'ks': ['UT_Segment_member', 'emi@split.io', 'other'] }), + JSON.stringify({ 'f': 'UT_NOT_IN_SEGMENT', 'ks': ['UT_Segment_member', 'emi@split.io'] }), + JSON.stringify({ 'f': 'UT_SET_MATCHER', 'ks': ['UT_Segment_member'] }), + JSON.stringify({ 'f': 'UT_NOT_SET_MATCHER', 'ks': ['UT_Segment_member'] }), + JSON.stringify({ 'f': 'always-o.n-with-config', 'ks': ['UT_Segment_member'] }), + JSON.stringify({ 'f': 'always-on', 'ks': ['UT_Segment_member'] }), + JSON.stringify({ 'f': 'hierarchical_splits_testing_on', 'ks': ['UT_Segment_member'] }), + JSON.stringify({ 'f': 'hierarchical_splits_testing_off', 'ks': ['UT_Segment_member'] }), + JSON.stringify({ 'f': 'hierarchical_splits_testing_on_negated', 'ks': ['UT_Segment_member'] }), + ]; + assert.deepEqual(trackedUniqueKeys, expectedUniqueKeys); + + // Assert impressionsListener + setTimeout(() => { + assert.equal(impressions.length, TOTAL_RAW_IMPRESSIONS, 'Each evaluation has its corresponting impression'); + assert.equal(impressions[0].impression.label, SDK_NOT_READY, 'The first impression is control with label "sdk not ready"'); + + assert.end(); + }); + }); + t.test('Connection timeout and then ready', assert => { const connDelay = 110; const readyTimeout = 0.1; // 100 millis From 82c20f2cdfb65fe71880ea9d65b103d52c7f4797 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 19 Sep 2022 19:46:45 -0300 Subject: [PATCH 3/9] updated e2e tests for partial consumer mode with pluggable storage to validate NONE impressions mode --- .../consumer/browser_consumer_partial.spec.js | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/src/__tests__/consumer/browser_consumer_partial.spec.js b/src/__tests__/consumer/browser_consumer_partial.spec.js index 81476ff..5c705ab 100644 --- a/src/__tests__/consumer/browser_consumer_partial.spec.js +++ b/src/__tests__/consumer/browser_consumer_partial.spec.js @@ -194,4 +194,151 @@ tape('Browser Consumer Partial mode with pluggable storage', function (t) { }); }); + t.test('Regular usage - NONE strategy', async (assert) => { + + fetchMock.postOnce(url(config, '/testImpressions/count'), 200); + fetchMock.postOnce(url(config, '/v1/keys/cs'), (url, req) => { + const data = JSON.parse(req.body); + + assert.deepEqual(data, { + keys: [ + { + k: 'UT_Segment_member', + fs: ['UT_IN_SEGMENT', 'UT_NOT_IN_SEGMENT', 'UT_SET_MATCHER', 'UT_NOT_SET_MATCHER', 'always-o.n-with-config', 'always-on', 'hierarchical_splits_testing_on', 'hierarchical_splits_testing_off', 'hierarchical_splits_testing_on_negated'] + }, + { + k: 'emi@split.io', + fs: ['UT_IN_SEGMENT', 'UT_NOT_IN_SEGMENT'] + }, + { + k: 'other', + fs: ['UT_IN_SEGMENT'] + }] + }, 'We performed evaluations for 3 keys, so we should have 3 items total.'); + + return 200; + }); + + fetchMock.postOnce(url(config, '/events/bulk'), (url, req) => { + const resp = JSON.parse(req.body); + assert.equal(resp.length, TOTAL_EVENTS, 'All successfully tracked events were sent'); + return 200; + }); + + // Load wrapper with data to do the proper tests + const wrapperInstance = inMemoryWrapperFactory(); + await applyOperations(wrapperInstance); + + /** @type SplitIO.ImpressionData[] */ + const impressions = []; + const disconnectSpy = sinon.spy(wrapperInstance, 'disconnect'); + + // Overwrite Math.random to NOT track telemetry + const originalMathRandom = Math.random; Math.random = () => 0.5; + const sdk = SplitFactory({ + ...config, + storage: PluggableStorage({ + prefix: wrapperPrefix, + wrapper: wrapperInstance + }), + sync: { + impressionsMode: 'NONE' + }, + impressionListener: { + logImpression(data) { impressions.push(data); } + } + }); + Math.random = originalMathRandom; // restore + + const client = sdk.client(); + const otherClient = sdk.client('emi@split.io'); + + /** Evaluation, track and manager methods before SDK_READY */ + + const getTreatmentResult = client.getTreatment('UT_IN_SEGMENT'); + + assert.equal(typeof getTreatmentResult.then, 'function', 'GetTreatment calls should always return a promise on Consumer mode.'); + assert.equal(await getTreatmentResult, 'control', 'Evaluations using pluggable storage should be control if initiated before SDK_READY.'); + assert.equal(client.__getStatus().isReadyFromCache, false, 'SDK in consumer mode is not operational inmediatelly'); + assert.equal(client.__getStatus().isReady, false, 'SDK in consumer mode is not operational inmediatelly'); + + const trackResult = otherClient.track('user', 'test.event', 18); + assert.equal(typeof trackResult.then, 'function', 'Track calls should always return a promise on Consumer mode.'); + assert.true(await trackResult, 'If the wrapper operation success to queue the event, the promise will resolve to true'); + + /** Evaluation, track and manager methods on SDK_READY */ + + await client.ready(); + await otherClient.ready(); // waiting to avoid 'control' with label 'sdk not ready' + + assert.equal(await client.getTreatment('UT_IN_SEGMENT'), 'on', '`getTreatment` evaluation using pluggable storage should be correct.'); + assert.equal((await otherClient.getTreatmentWithConfig('UT_IN_SEGMENT')).treatment, 'off', '`getTreatmentWithConfig` evaluation using pluggable storage should be correct.'); + + assert.equal((await client.getTreatments(['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'], 'off', '`getTreatments` evaluation using pluggable storage should be correct.'); + assert.equal((await otherClient.getTreatmentsWithConfig(['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'].treatment, 'on', '`getTreatmentsWithConfig` evaluation using pluggable storage should be correct.'); + + assert.equal(await client.getTreatment('UT_SET_MATCHER', { + permissions: ['admin'] + }), 'on', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('UT_SET_MATCHER', { + permissions: ['not_matching'] + }), 'off', 'Evaluations using pluggable storage should be correct.'); + + assert.equal(await client.getTreatment('UT_NOT_SET_MATCHER', { + permissions: ['create'] + }), 'off', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('UT_NOT_SET_MATCHER', { + permissions: ['not_matching'] + }), 'on', 'Evaluations using pluggable storage should be correct.'); + assert.deepEqual(await client.getTreatmentWithConfig('UT_NOT_SET_MATCHER', { + permissions: ['not_matching'] + }), { + treatment: 'on', + config: null + }, 'Evaluations using pluggable storage should be correct, including configs.'); + assert.deepEqual(await client.getTreatmentWithConfig('always-o.n-with-config'), { + treatment: 'o.n', + config: '{"color":"brown"}' + }, 'Evaluations using pluggable storage should be correct, including configs.'); + + assert.equal(await client.getTreatment('always-on'), 'on', 'Evaluations using pluggable storage should be correct.'); + + // Below splits were added manually. + // They are all_keys (always evaluate to on) which depend from always-on split. the _on/off is what treatment they are expecting there. + assert.equal(await client.getTreatment('hierarchical_splits_testing_on'), 'on', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('hierarchical_splits_testing_off'), 'off', 'Evaluations using pluggable storage should be correct.'); + assert.equal(await client.getTreatment('hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using pluggable storage should be correct.'); + + assert.equal(typeof client.track('user', 'test.event', 18).then, 'function', 'Track calls should always return a promise on Consumer mode.'); + assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Consumer mode, even when parameters are incorrect.'); + + assert.true(await client.track('user', 'test.event', 18), 'If the event was succesfully queued the promise will resolve to true'); + assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false'); + + // New shared client created + const newClient = sdk.client('other'); + newClient.track('user', 'test.event', 18).then(result => assert.true(result, 'Track should attempt to track, whether SDK_READY has been emitted or not.')); + newClient.getTreatment('UT_IN_SEGMENT').then(result => assert.equal(result, 'control', '`getTreatment` evaluation is control if shared client is not ready yet.')); + + await newClient.ready(); + assert.true(await newClient.track('user', 'test.event', 18), 'Track should attempt to track, whether SDK_READY has been emitted or not.'); + assert.equal((await newClient.getTreatment('UT_IN_SEGMENT')), 'off', '`getTreatment` evaluation using pluggable storage should be correct.'); + + await client.ready(); // promise already resolved + await newClient.destroy(); + await otherClient.destroy(); + + await client.destroy(); + + assert.equal(disconnectSpy.callCount, 1, 'Wrapper disconnect method should be called only once, when the main client is destroyed'); + + // Assert impressionsListener + setTimeout(() => { + assert.equal(impressions.length, TOTAL_RAW_IMPRESSIONS, 'Each evaluation has its corresponting impression'); + assert.equal(impressions[0].impression.label, SDK_NOT_READY, 'The first impression is control with label "sdk not ready"'); + + assert.end(); + }); + }); + }); From 8c24dafc529ed9fce8924a86ed886ce4ac8efdab Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 19 Sep 2022 19:53:38 -0300 Subject: [PATCH 4/9] rc --- .github/workflows/ci-cd.yml | 4 ++-- package-lock.json | 8 ++++---- package.json | 4 ++-- src/__tests__/consumer/browser_consumer.spec.js | 2 +- src/settings/defaults.ts | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 86739af..1cb610c 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -42,14 +42,14 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") BUILD_COMMIT=${{ github.sha }} npm run build - name: Configure AWS credentials (development) - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/pluggable_storage_mtk_producer_api' }} uses: aws-actions/configure-aws-credentials@v1 with: role-to-assume: arn:aws:iam::079419646996:role/public-assets aws-region: us-east-1 - name: Upload to S3 (development) - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/pluggable_storage_mtk_producer_api' }} run: aws s3 sync $SOURCE_DIR s3://$BUCKET/$DEST_DIR $ARGS env: BUCKET: split-public-stage diff --git a/package-lock.json b/package-lock.json index 742795d..32c273a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "0.8.0", + "version": "0.8.1-rc.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2194,9 +2194,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.6.2-rc.9", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.2-rc.9.tgz", - "integrity": "sha512-pUTAuhZlkTBgs5nGu5in4jDdlvj0yPRdnqXWUjNml4+GmRuUx827R8FL07jFHV2m88K9BbqZfpztsgbis5hRHQ==", + "version": "1.6.2-rc.10", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.2-rc.10.tgz", + "integrity": "sha512-Z6zNqaBO9XraodPdaRw7+fIx0ngh2Fs9slhnN/eFgeX91im649trhS/jW3542ik0vggVUEG6SN38g8o7dChuhg==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index a35a08c..c1cfbe8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "0.8.0", + "version": "0.8.1-rc.1", "description": "Split SDK for Javascript on Browser", "main": "cjs/index.js", "module": "esm/index.js", @@ -63,7 +63,7 @@ "bugs": "https://github.com/splitio/javascript-browser-client/issues", "homepage": "https://github.com/splitio/javascript-browser-client#readme", "dependencies": { - "@splitsoftware/splitio-commons": "1.6.2-rc.9", + "@splitsoftware/splitio-commons": "1.6.2-rc.10", "@types/google.analytics": "0.0.40" }, "devDependencies": { diff --git a/src/__tests__/consumer/browser_consumer.spec.js b/src/__tests__/consumer/browser_consumer.spec.js index a601fb1..c2685f6 100644 --- a/src/__tests__/consumer/browser_consumer.spec.js +++ b/src/__tests__/consumer/browser_consumer.spec.js @@ -300,7 +300,7 @@ tape('Browser Consumer mode with pluggable storage', function (t) { const expectedImpressionCount = [ [`${wrapperPrefix}.SPLITIO.impressions.count::UT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '1'], [`${wrapperPrefix}.SPLITIO.impressions.count::UT_NOT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '1'], - ] + ]; assert.deepEqual(trackedImpressionCounts, expectedImpressionCount, 'Impression counts should be present in the external storage'); // Assert impressionsListener diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 5a9b687..9f52a0b 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -2,7 +2,7 @@ import { LogLevels, isLogLevelString } from '@splitsoftware/splitio-commons/src/ import { ConsentStatus, LogLevel } from '@splitsoftware/splitio-commons/src/types'; import { CONSENT_GRANTED } from '@splitsoftware/splitio-commons/src/utils/constants'; -const packageVersion = '0.8.0'; +const packageVersion = '0.8.1-rc.1'; /** * In browser, the default debug level, can be set via the `localStorage.splitio_debug` item. From 8f75cc457b84734025224e38a3e83422333f55c7 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 27 Sep 2022 22:38:35 -0300 Subject: [PATCH 5/9] rc --- package-lock.json | 8 ++++---- package.json | 5 +++-- src/settings/defaults.ts | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 32c273a..973a2f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "0.8.1-rc.1", + "version": "0.8.1-rc.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2194,9 +2194,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.6.2-rc.10", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.2-rc.10.tgz", - "integrity": "sha512-Z6zNqaBO9XraodPdaRw7+fIx0ngh2Fs9slhnN/eFgeX91im649trhS/jW3542ik0vggVUEG6SN38g8o7dChuhg==", + "version": "1.6.2-rc.13", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.2-rc.13.tgz", + "integrity": "sha512-QwK7EaBCp+r0/Bizk/yvNJXUkIrY5Du3EJVkg6C/yXN7FmgZqBXXjSp2EoP/YGMh2q6SWWv7L5Gfxb6d0TuGSg==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index c1cfbe8..8c550ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "0.8.1-rc.1", + "version": "0.8.1-rc.2", "description": "Split SDK for Javascript on Browser", "main": "cjs/index.js", "module": "esm/index.js", @@ -42,6 +42,7 @@ "pretest-ts-decls": "npm run build:esm && npm run build:cjs && npm link", "test-ts-decls": "./scripts/ts-tests.sh", "posttest-ts-decls": "npm unlink && npm install", + "all": "npm run check && npm run build && npm run test-ts-decls && npm run test", "publish:rc": "npm run check && npm run build && npm publish --tag rc", "publish:stable": "npm run check && npm run build && npm publish" }, @@ -63,7 +64,7 @@ "bugs": "https://github.com/splitio/javascript-browser-client/issues", "homepage": "https://github.com/splitio/javascript-browser-client#readme", "dependencies": { - "@splitsoftware/splitio-commons": "1.6.2-rc.10", + "@splitsoftware/splitio-commons": "1.6.2-rc.13", "@types/google.analytics": "0.0.40" }, "devDependencies": { diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 9f52a0b..94a343b 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -2,7 +2,7 @@ import { LogLevels, isLogLevelString } from '@splitsoftware/splitio-commons/src/ import { ConsentStatus, LogLevel } from '@splitsoftware/splitio-commons/src/types'; import { CONSENT_GRANTED } from '@splitsoftware/splitio-commons/src/utils/constants'; -const packageVersion = '0.8.1-rc.1'; +const packageVersion = '0.8.1-rc.2'; /** * In browser, the default debug level, can be set via the `localStorage.splitio_debug` item. From dd52383ae54fc9248892d1d0e6088da375f7406e Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 29 Sep 2022 17:14:08 -0300 Subject: [PATCH 6/9] upgrade js-commons and prepare rc --- .eslintrc | 3 ++- package-lock.json | 8 ++++---- package.json | 4 ++-- src/settings/defaults.ts | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.eslintrc b/.eslintrc index dabb965..9954be9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -30,7 +30,8 @@ "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "error", "keyword-spacing": "error", - "comma-style": "error" + "comma-style": "error", + "no-trailing-spaces": "error" }, "overrides": [ diff --git a/package-lock.json b/package-lock.json index 973a2f0..78a359a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "0.8.1-rc.2", + "version": "0.8.1-rc.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2194,9 +2194,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.6.2-rc.13", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.2-rc.13.tgz", - "integrity": "sha512-QwK7EaBCp+r0/Bizk/yvNJXUkIrY5Du3EJVkg6C/yXN7FmgZqBXXjSp2EoP/YGMh2q6SWWv7L5Gfxb6d0TuGSg==", + "version": "1.6.2-rc.14", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.2-rc.14.tgz", + "integrity": "sha512-cslzWAu0ony85y0SUcXa3vRhES3pwYDROuke79IjyH0UDunr30kmPwXRWONFS+t4KU+9N4ABNNOKgrX/B5sv9Q==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index 8c550ef..a533f74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "0.8.1-rc.2", + "version": "0.8.1-rc.3", "description": "Split SDK for Javascript on Browser", "main": "cjs/index.js", "module": "esm/index.js", @@ -64,7 +64,7 @@ "bugs": "https://github.com/splitio/javascript-browser-client/issues", "homepage": "https://github.com/splitio/javascript-browser-client#readme", "dependencies": { - "@splitsoftware/splitio-commons": "1.6.2-rc.13", + "@splitsoftware/splitio-commons": "1.6.2-rc.14", "@types/google.analytics": "0.0.40" }, "devDependencies": { diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 94a343b..13e5bcb 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -2,7 +2,7 @@ import { LogLevels, isLogLevelString } from '@splitsoftware/splitio-commons/src/ import { ConsentStatus, LogLevel } from '@splitsoftware/splitio-commons/src/types'; import { CONSENT_GRANTED } from '@splitsoftware/splitio-commons/src/utils/constants'; -const packageVersion = '0.8.1-rc.2'; +const packageVersion = '0.8.1-rc.3'; /** * In browser, the default debug level, can be set via the `localStorage.splitio_debug` item. From fd9caedbf8dbd9c8684da68fa5a5c4759a4eb6fd Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 29 Sep 2022 18:55:23 -0300 Subject: [PATCH 7/9] fix e2e test --- src/__tests__/consumer/browser_consumer.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/__tests__/consumer/browser_consumer.spec.js b/src/__tests__/consumer/browser_consumer.spec.js index c2685f6..1144853 100644 --- a/src/__tests__/consumer/browser_consumer.spec.js +++ b/src/__tests__/consumer/browser_consumer.spec.js @@ -430,10 +430,10 @@ tape('Browser Consumer mode with pluggable storage', function (t) { const trackedImpressionCounts = await wrapperInstance.getKeysByPrefix('PLUGGABLE_STORAGE_UT.SPLITIO.impressions.count') .then(impressionCountsKeys => Promise.all(impressionCountsKeys.map(key => wrapperInstance.get(key).then(count => ([key, count]))))); const expectedImpressionCount = [ - [`${wrapperPrefix}.SPLITIO.impressions.count::UT_IN_SEGMENT::${truncateTimeFrame(timeFrame)}`, '1'], - [`${wrapperPrefix}.SPLITIO.impressions.count::UT_NOT_IN_SEGMENT::${truncateTimeFrame(timeFrame)}`, '1'], - [`${wrapperPrefix}.SPLITIO.impressions.count::UT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '1'], - [`${wrapperPrefix}.SPLITIO.impressions.count::UT_NOT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '1'], + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_IN_SEGMENT::${truncateTimeFrame(timeFrame)}`, '5'], + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_NOT_IN_SEGMENT::${truncateTimeFrame(timeFrame)}`, '2'], + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '3'], + [`${wrapperPrefix}.SPLITIO.impressions.count::UT_NOT_SET_MATCHER::${truncateTimeFrame(timeFrame)}`, '3'], [`${wrapperPrefix}.SPLITIO.impressions.count::always-o.n-with-config::${truncateTimeFrame(timeFrame)}`, '1'], [`${wrapperPrefix}.SPLITIO.impressions.count::always-on::${truncateTimeFrame(timeFrame)}`, '1'], [`${wrapperPrefix}.SPLITIO.impressions.count::hierarchical_splits_testing_on::${truncateTimeFrame(timeFrame)}`, '1'], From 6acac871f6c9b690fe10db813a55426344901f39 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 30 Sep 2022 14:56:52 -0300 Subject: [PATCH 8/9] update type definitions comment regarding new NONE impression mode option --- .github/workflows/ci-cd.yml | 4 ++-- types/splitio.d.ts | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 1cb610c..86739af 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -42,14 +42,14 @@ jobs: run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") BUILD_COMMIT=${{ github.sha }} npm run build - name: Configure AWS credentials (development) - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/pluggable_storage_mtk_producer_api' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} uses: aws-actions/configure-aws-credentials@v1 with: role-to-assume: arn:aws:iam::079419646996:role/public-assets aws-region: us-east-1 - name: Upload to S3 (development) - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/pluggable_storage_mtk_producer_api' }} + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/development' }} run: aws s3 sync $SOURCE_DIR s3://$BUCKET/$DEST_DIR $ARGS env: BUCKET: split-public-stage diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 9c1a89f..87ab9e0 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -217,9 +217,11 @@ interface ISharedSettings { splitFilters?: SplitIO.SplitFilter[] /** * Impressions Collection Mode. Option to determine how impressions are going to be sent to Split Servers. - * Possible values are 'DEBUG' and 'OPTIMIZED'. + * Possible values are 'DEBUG', 'OPTIMIZED', and 'NONE'. * - DEBUG: will send all the impressions generated (recommended only for debugging purposes). - * - OPTIMIZED: will send unique impressions to Split Servers avoiding a considerable amount of traffic that duplicated impressions could generate. + * - OPTIMIZED: will send unique impressions to Split Servers, avoiding a considerable amount of traffic that duplicated impressions could generate. + * - NONE: will send unique keys evaluated per feature to Split Servers, avoiding a considerable amount of traffic that impressions could generate. + * * @property {String} impressionsMode * @default 'OPTIMIZED' */ From 8253d0d814be6faacba3a261bc9d792a6c80c58b Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 4 Oct 2022 19:59:06 -0300 Subject: [PATCH 9/9] update changelog and js-commons version for stable release --- CHANGES.txt | 5 +++-- package-lock.json | 8 ++++---- package.json | 4 ++-- src/settings/defaults.ts | 2 +- types/splitio.d.ts | 2 +- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 213a27b..d35c78b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ -0.8.1 (September XXX, 2022) -- Updated @splitsoftware/splitio-commons package to version 1.6.2, that improves the performance of split evaluations (i.e., `getTreatment(s)` method calls) when using the default storage in memory. +0.9.0 (October 5, 2022) +- Added a new impressions mode for the SDK called NONE, to be used in factory when there is no desire to capture impressions on an SDK factory to feed Split's analytics engine. Running NONE mode, the SDK will only capture unique keys evaluated for a particular feature flag instead of full blown impressions. +- Updated @splitsoftware/splitio-commons package to version 1.7.0, that improves the performance of split evaluations (i.e., `getTreatment(s)` method calls) when using the default storage in memory, among other improvements. 0.8.0 (July 22, 2022) - Added `autoRequire` configuration option to the Google Analytics to Split integration, which takes care of requiring the splitTracker plugin on trackers dynamically created by Google tag managers (See https://help.split.io/hc/en-us/articles/360040838752#set-up-with-gtm-and-gtag.js). diff --git a/package-lock.json b/package-lock.json index 78a359a..3838356 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "0.8.1-rc.3", + "version": "0.9.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -2194,9 +2194,9 @@ "dev": true }, "@splitsoftware/splitio-commons": { - "version": "1.6.2-rc.14", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.6.2-rc.14.tgz", - "integrity": "sha512-cslzWAu0ony85y0SUcXa3vRhES3pwYDROuke79IjyH0UDunr30kmPwXRWONFS+t4KU+9N4ABNNOKgrX/B5sv9Q==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.7.0.tgz", + "integrity": "sha512-QjDcSV2mbwkhMkA9zutmdU4NwY/Hb09EZeXFCmRxWHHYLegl6XWztbghqD6fw3Q5yb7jsQ+wn7nMEZsvNKum7g==", "requires": { "tslib": "^2.3.1" } diff --git a/package.json b/package.json index a533f74..e85665d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-browserjs", - "version": "0.8.1-rc.3", + "version": "0.9.0", "description": "Split SDK for Javascript on Browser", "main": "cjs/index.js", "module": "esm/index.js", @@ -64,7 +64,7 @@ "bugs": "https://github.com/splitio/javascript-browser-client/issues", "homepage": "https://github.com/splitio/javascript-browser-client#readme", "dependencies": { - "@splitsoftware/splitio-commons": "1.6.2-rc.14", + "@splitsoftware/splitio-commons": "1.7.0", "@types/google.analytics": "0.0.40" }, "devDependencies": { diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 13e5bcb..60aaad6 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -2,7 +2,7 @@ import { LogLevels, isLogLevelString } from '@splitsoftware/splitio-commons/src/ import { ConsentStatus, LogLevel } from '@splitsoftware/splitio-commons/src/types'; import { CONSENT_GRANTED } from '@splitsoftware/splitio-commons/src/utils/constants'; -const packageVersion = '0.8.1-rc.3'; +const packageVersion = '0.9.0'; /** * In browser, the default debug level, can be set via the `localStorage.splitio_debug` item. diff --git a/types/splitio.d.ts b/types/splitio.d.ts index 87ab9e0..a1e9d14 100644 --- a/types/splitio.d.ts +++ b/types/splitio.d.ts @@ -220,7 +220,7 @@ interface ISharedSettings { * Possible values are 'DEBUG', 'OPTIMIZED', and 'NONE'. * - DEBUG: will send all the impressions generated (recommended only for debugging purposes). * - OPTIMIZED: will send unique impressions to Split Servers, avoiding a considerable amount of traffic that duplicated impressions could generate. - * - NONE: will send unique keys evaluated per feature to Split Servers, avoiding a considerable amount of traffic that impressions could generate. + * - NONE: will send unique keys evaluated per feature to Split Servers instead of full blown impressions, avoiding a considerable amount of traffic that impressions could generate. * * @property {String} impressionsMode * @default 'OPTIMIZED'