From b4b4fe90c9cce6bf3a5e57bd780095aa2fe88628 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 12 May 2025 16:26:09 -0300 Subject: [PATCH 01/12] Upgrade JS-commons to v2.0.0 --- CHANGES.txt | 3 ++- package-lock.json | 25 ++++++++++----------- package.json | 2 +- src/Synchronizer.ts | 6 ++--- src/settings/defaults.ts | 5 +++++ src/settings/index.ts | 3 +-- src/storages/synchronizerStorage.ts | 1 + src/submitters/impressionCountsSubmitter.ts | 3 +-- src/submitters/impressionsSubmitter.ts | 7 +++--- src/submitters/telemetrySubmitter.ts | 5 ++--- src/submitters/uniqueKeysSubmitter.ts | 5 ++--- 11 files changed, 33 insertions(+), 32 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d4af3fa..e7e47a0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ -0.7.1 (May XXX, 2025) +1.0.0 (May XXX, 2025) + - Updated @splitsoftware/splitio-commons package to version 2.0.0. - Updated some transitive dependencies for vulnerability fixes. 0.7.0 (August 5, 2024) diff --git a/package-lock.json b/package-lock.json index e78b191..0c1b90e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.7.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "1.16.0", + "@splitsoftware/splitio-commons": "2.0.0", "dotenv": "^9.0.1", "node-fetch": "^2.6.7", "yargs": "^17.0.1" @@ -1672,10 +1672,12 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.16.0.tgz", - "integrity": "sha512-k16cCWJOWut/NB5W1d9hQEYPxFrZXO66manp+8d6RjZYH4r+Q6lu82NYjDcfh5E93H9v+TVKcQLAmpVofbjcvg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", + "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", + "license": "Apache-2.0", "dependencies": { + "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" }, "peerDependencies": { @@ -1779,7 +1781,6 @@ "version": "4.28.10", "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -1833,8 +1834,7 @@ "node_modules/@types/node": { "version": "15.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", - "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==", - "dev": true + "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==" }, "node_modules/@types/node-fetch": { "version": "2.6.3", @@ -11025,10 +11025,11 @@ } }, "@splitsoftware/splitio-commons": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.16.0.tgz", - "integrity": "sha512-k16cCWJOWut/NB5W1d9hQEYPxFrZXO66manp+8d6RjZYH4r+Q6lu82NYjDcfh5E93H9v+TVKcQLAmpVofbjcvg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", + "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", "requires": { + "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" } }, @@ -11104,7 +11105,6 @@ "version": "4.28.10", "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", - "dev": true, "requires": { "@types/node": "*" } @@ -11158,8 +11158,7 @@ "@types/node": { "version": "15.14.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", - "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==", - "dev": true + "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==" }, "@types/node-fetch": { "version": "2.6.3", diff --git a/package.json b/package.json index 58fa0a4..e68cb22 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "prepublishOnly": "npm run check && npm run test && npm run build" }, "dependencies": { - "@splitsoftware/splitio-commons": "1.16.0", + "@splitsoftware/splitio-commons": "2.0.0", "dotenv": "^9.0.1", "node-fetch": "^2.6.7", "yargs": "^17.0.1" diff --git a/src/Synchronizer.ts b/src/Synchronizer.ts index 72b6c5b..0bd2927 100644 --- a/src/Synchronizer.ts +++ b/src/Synchronizer.ts @@ -88,11 +88,11 @@ export class Synchronizer { * The Split's HTTPclient, required to make the requests to the API. */ this._splitApi = splitApiFactory( - this.settings, // @ts-expect-error + this.settings, { - getFetch: Synchronizer._getFetch, + getFetch: Synchronizer._getFetch, // @ts-expect-error getOptions(settings: ISettings) { - // @ts-expect-error + // User provided options take precedence if (settings.sync.requestOptions) return settings.sync.requestOptions; }, }, diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 46183fa..5265c77 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -4,6 +4,8 @@ */ const version = '@VERSION@'; +export const FLAG_SPEC_VERSION = '1.1'; + /** * Default values to create settings for the Javascrip Synchronizer. */ @@ -28,6 +30,9 @@ export const defaults = { // Number of retry attempts for posting impressions and events. maxRetries: 3, }, + sync: { + flagSpecVersion: FLAG_SPEC_VERSION, + }, version: `synctoolsjs-${version}`, streamingEnabled: false, }; diff --git a/src/settings/index.ts b/src/settings/index.ts index 0ad6c55..15ec510 100644 --- a/src/settings/index.ts +++ b/src/settings/index.ts @@ -1,11 +1,10 @@ import { ILogger } from '@splitsoftware/splitio-commons/src/logger/types'; import { ISettings } from '@splitsoftware/splitio-commons/src/types'; -import { FLAG_SPEC_VERSION } from '@splitsoftware/splitio-commons/src/utils/constants'; import { isIntegerNumber } from '@splitsoftware/splitio-commons/src/utils/lang'; import { settingsValidation } from '@splitsoftware/splitio-commons/src/utils/settingsValidation/index'; import { validateLogger } from '@splitsoftware/splitio-commons/src/utils/settingsValidation/logger/builtinLogger'; import { ISynchronizerSettings } from '../../types'; -import { defaults } from './defaults'; +import { defaults, FLAG_SPEC_VERSION } from './defaults'; const FLAG_SPEC_VERSIONS = ['1.0', FLAG_SPEC_VERSION]; diff --git a/src/storages/synchronizerStorage.ts b/src/storages/synchronizerStorage.ts index f24c685..c8fe960 100644 --- a/src/storages/synchronizerStorage.ts +++ b/src/storages/synchronizerStorage.ts @@ -14,6 +14,7 @@ export function synchronizerStorageFactory(settings: ISettings, onReadyCb: IStor const { storage } = settings; // @TODO support both storage param types?: config object (JS SDK) and storage function (Browser and RN SDK) + // @ts-expect-error const storageFactory = typeof storage === 'function' ? storage : PluggableStorage(storage); // Ignoring metadata parameter since it's use by the Consumer API (like Events.track) // and the Synchronizer doesn't need to perform such actions. diff --git a/src/submitters/impressionCountsSubmitter.ts b/src/submitters/impressionCountsSubmitter.ts index 11df007..4b20e04 100644 --- a/src/submitters/impressionCountsSubmitter.ts +++ b/src/submitters/impressionCountsSubmitter.ts @@ -6,11 +6,10 @@ import { ILogger } from '@splitsoftware/splitio-commons/src/logger/types'; import { ImpressionCountsCachePluggable } from '@splitsoftware/splitio-commons/src/storages/pluggable/ImpressionCountsCachePluggable'; import { submitterFactory } from './submitter'; import { ImpressionCountsPayload } from '@splitsoftware/splitio-commons/src/sync/submitters/types'; -import { _Map } from '@splitsoftware/splitio-commons/src/utils/lang/maps'; // Merge impressions counts objects function merge(counts1: ImpressionCountsPayload, counts2: ImpressionCountsPayload): ImpressionCountsPayload { - const merged = new _Map(counts1.pf.map((count) => [count.f + count.m, count])); + const merged = new Map(counts1.pf.map((count) => [count.f + count.m, count])); counts2.pf.forEach((count) => { const key = count.f + count.m; if (merged.has(key)) merged.get(key)!.rc += count.rc; diff --git a/src/submitters/impressionsSubmitter.ts b/src/submitters/impressionsSubmitter.ts index 46831f7..94348f1 100644 --- a/src/submitters/impressionsSubmitter.ts +++ b/src/submitters/impressionsSubmitter.ts @@ -4,16 +4,15 @@ import { StoredImpressionWithMetadata } from '@splitsoftware/splitio-commons/src import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time'; import { ImpressionObserver } from '@splitsoftware/splitio-commons/src/trackers/impressionObserver/ImpressionObserver'; import { groupBy, metadataToHeaders } from './utils'; -import { SplitIO } from '@splitsoftware/splitio-commons/src/types'; +import type SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import { ILogger } from '@splitsoftware/splitio-commons/src/logger/types'; import { IMetadata } from '@splitsoftware/splitio-commons/src/dtos/types'; -import { ImpressionDTO } from '@splitsoftware/splitio-commons/src/types'; import { ImpressionsPayload } from '@splitsoftware/splitio-commons/src/sync/submitters/types'; import { submitterWithMetadataFactory } from './submitter'; export type ImpressionsDTOWithMetadata = { metadata: IMetadata; - impression: ImpressionDTO; + impression: SplitIO.ImpressionDTO; } /** @@ -49,7 +48,7 @@ export const impressionWithMetadataToImpressionDTO = (storedImpression: StoredIm changeNumber: i.c, time: i.m, pt: i.pt, - } as ImpressionDTO, + } as SplitIO.ImpressionDTO, }; }; diff --git a/src/submitters/telemetrySubmitter.ts b/src/submitters/telemetrySubmitter.ts index 8bdadc1..aa864c2 100644 --- a/src/submitters/telemetrySubmitter.ts +++ b/src/submitters/telemetrySubmitter.ts @@ -3,7 +3,6 @@ import { TelemetryUsageStats } from '@splitsoftware/splitio-commons/src/sync/sub import { ITelemetryCacheAsync } from '@splitsoftware/splitio-commons/src/storages/types'; import { metadataToHeaders } from './utils'; import { ISplitApi } from '@splitsoftware/splitio-commons/src/services/types'; -import { _Map, IMap } from '@splitsoftware/splitio-commons/src/utils/lang/maps'; /** @@ -22,11 +21,11 @@ export function telemetrySubmitterFactory( telemetryCache: ITelemetryCacheAsync, ): () => Promise { - async function buildUsageStats(): Promise> { + async function buildUsageStats(): Promise> { const latencies = await telemetryCache.popLatencies(); const exceptions = await telemetryCache.popExceptions(); - const result = new _Map(); + const result = new Map(); latencies.forEach((latency, metadata) => { result.set(metadata, { mL: latency }); diff --git a/src/submitters/uniqueKeysSubmitter.ts b/src/submitters/uniqueKeysSubmitter.ts index 216f263..30cd718 100644 --- a/src/submitters/uniqueKeysSubmitter.ts +++ b/src/submitters/uniqueKeysSubmitter.ts @@ -3,7 +3,6 @@ import { ILogger } from '@splitsoftware/splitio-commons/src/logger/types'; import { IPostUniqueKeysBulkSs } from '@splitsoftware/splitio-commons/src/services/types'; import { fromUniqueKeysCollector } from '@splitsoftware/splitio-commons/src/storages/inMemory/UniqueKeysCacheInMemory'; import { UniqueKeysCachePluggable } from '@splitsoftware/splitio-commons/src/storages/pluggable/UniqueKeysCachePluggable'; -import { ISet, _Set } from '@splitsoftware/splitio-commons/src/utils/lang/sets'; import { submitterFactory } from './submitter'; export function uniqueKeysSubmitterFactory( @@ -19,12 +18,12 @@ export function uniqueKeysSubmitterFactory( if (!uniqueKeyItems.length) return undefined; - const mergedUniqueKeys = uniqueKeyItems.reduce<{ [featureName: string]: ISet }>((accUniqueKeys, uniqueKeyItem) => { + const mergedUniqueKeys = uniqueKeyItems.reduce<{ [featureName: string]: Set }>((accUniqueKeys, uniqueKeyItem) => { const featureNameKeys = accUniqueKeys[uniqueKeyItem.f]; if (featureNameKeys) { uniqueKeyItem.ks.forEach(key => featureNameKeys.add(key)); } else { - accUniqueKeys[uniqueKeyItem.f] = new _Set(uniqueKeyItem.ks); + accUniqueKeys[uniqueKeyItem.f] = new Set(uniqueKeyItem.ks); } return accUniqueKeys; }, {}); From db52936ab60f71c4293450b0b9567a6e89e5a8a5 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Mon, 12 May 2025 17:19:26 -0300 Subject: [PATCH 02/12] Upgrade JS-commons to v2.2.0 --- CHANGES.txt | 2 +- package-lock.json | 14 +-- package.json | 2 +- src/Synchronizer.ts | 8 +- .../__tests__/impressionsMockUtils.ts | 14 +-- .../__tests__/impressionsSubmitter.spec.ts | 1 + src/submitters/impressionsSubmitter.ts | 4 +- src/synchronizers/SplitsSynchronizer.ts | 69 +++++-------- .../__tests__/SplitsSynchronizer.spec.ts | 97 +++++-------------- 9 files changed, 70 insertions(+), 141 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index e7e47a0..b221dc2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,5 @@ 1.0.0 (May XXX, 2025) - - Updated @splitsoftware/splitio-commons package to version 2.0.0. + - Updated @splitsoftware/splitio-commons package to version 2.2.0. - Updated some transitive dependencies for vulnerability fixes. 0.7.0 (August 5, 2024) diff --git a/package-lock.json b/package-lock.json index 0c1b90e..59764f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.7.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.0.0", + "@splitsoftware/splitio-commons": "2.2.0", "dotenv": "^9.0.1", "node-fetch": "^2.6.7", "yargs": "^17.0.1" @@ -1672,9 +1672,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", - "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", + "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -11025,9 +11025,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.0.0.tgz", - "integrity": "sha512-Sz4+vFacl29xw3451z9IUgB4zBFKUWZdCnmOB0DDXA803YKPqjXphdAwN6nV+1vsX9pXV/OS6UaNC4oUICa6PA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", + "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" diff --git a/package.json b/package.json index e68cb22..9014cf5 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "prepublishOnly": "npm run check && npm run test && npm run build" }, "dependencies": { - "@splitsoftware/splitio-commons": "2.0.0", + "@splitsoftware/splitio-commons": "2.2.0", "dotenv": "^9.0.1", "node-fetch": "^2.6.7", "yargs": "^17.0.1" diff --git a/src/Synchronizer.ts b/src/Synchronizer.ts index 0bd2927..c32054d 100644 --- a/src/Synchronizer.ts +++ b/src/Synchronizer.ts @@ -15,7 +15,6 @@ import { impressionCountsSubmitterFactory } from './submitters/impressionCountsS import { synchronizerSettingsValidator } from './settings'; import { validateApiKey } from '@splitsoftware/splitio-commons/src/utils/inputValidation'; import { ISynchronizerSettings } from '../types'; -import { InMemoryStorageFactory } from '@splitsoftware/splitio-commons/src/storages/inMemory/InMemoryStorage'; import { IEventsCacheAsync } from '@splitsoftware/splitio-commons/src/storages/types'; import { IImpressionsCacheAsync } from '@splitsoftware/splitio-commons/src/storages/types'; import { telemetrySubmitterFactory } from './submitters/telemetrySubmitter'; @@ -144,12 +143,7 @@ export class Synchronizer { this._splitsSynchronizer = new SplitsSynchronizer( this._splitApi.fetchSplitChanges, this.settings, - this._storage.splits, - this._storage.segments, - // @ts-ignore - InMemoryStorageFactory({ settings: this.settings }), - // @ts-ignore - InMemoryStorageFactory({ settings: this.settings }) + this._storage, ); this._eventsSubmitter = eventsSubmitterFactory( this.settings.log, diff --git a/src/submitters/__tests__/impressionsMockUtils.ts b/src/submitters/__tests__/impressionsMockUtils.ts index 1764393..6ac3a35 100644 --- a/src/submitters/__tests__/impressionsMockUtils.ts +++ b/src/submitters/__tests__/impressionsMockUtils.ts @@ -1,11 +1,11 @@ +import type SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import { StoredImpressionWithMetadata } from '@splitsoftware/splitio-commons/src/sync/submitters/types'; -import { ImpressionDTO } from '@splitsoftware/splitio-commons/src/types'; import { _getRandomString } from './commonUtils'; /** * An Impression example. */ -const impressionFullNameNoMetadata: ImpressionDTO = { +const impressionFullNameNoMetadata: SplitIO.ImpressionDTO = { keyName: 'marcio@split.io', bucketingKey: 'impr_bucketing_2', feature: 'qc_team', @@ -13,6 +13,7 @@ const impressionFullNameNoMetadata: ImpressionDTO = { label: 'default rule', changeNumber: 828282828282, time: Date.now(), + properties: '{"key":"value"}', }; /** * An Impression with Metadata example. @@ -31,6 +32,7 @@ const impressionWithMetadata: StoredImpressionWithMetadata = { b: 'impr_bucketing_2', r: 'default rule', c: 828282828282, + properties: '{"key":"value"}', }, }; /** @@ -53,11 +55,11 @@ function getRandomiseMetadata(): StoredImpressionWithMetadata { * @returns {StoredImpressionWithMetadata} */ function getRandomiseImpression(): StoredImpressionWithMetadata { - const { k, t, m, b, r, c } = impressionWithMetadata.i; + const { k, t, m, b, r, c, properties } = impressionWithMetadata.i; return Object.assign( {}, impressionWithMetadata, - { i: { k, t, m, b, r, c, f: _getRandomString(12) } } + { i: { k, t, m, b, r, c, properties, f: _getRandomString(12) } } ); } /** @@ -65,7 +67,7 @@ function getRandomiseImpression(): StoredImpressionWithMetadata { * * @returns {ImpressionDTO} */ -export function getImpressionSampleWithNoMetadata(): ImpressionDTO { +export function getImpressionSampleWithNoMetadata(): SplitIO.ImpressionDTO { return impressionFullNameNoMetadata; } /** @@ -88,5 +90,3 @@ export function getImpressionsListWithSameMetadata( return [...Array(len).keys()].map(() => _impressionTarget); } - - diff --git a/src/submitters/__tests__/impressionsSubmitter.spec.ts b/src/submitters/__tests__/impressionsSubmitter.spec.ts index bbc7732..13fb8a8 100644 --- a/src/submitters/__tests__/impressionsSubmitter.spec.ts +++ b/src/submitters/__tests__/impressionsSubmitter.spec.ts @@ -242,6 +242,7 @@ describe('Impressions Submitter for Lightweight Synchronizer', () => { c: expect.any(Number), m: expect.any(Number), pt: expect.any(Number), + properties: expect.any(String), }) ); // @ts-ignore diff --git a/src/submitters/impressionsSubmitter.ts b/src/submitters/impressionsSubmitter.ts index 94348f1..e20021d 100644 --- a/src/submitters/impressionsSubmitter.ts +++ b/src/submitters/impressionsSubmitter.ts @@ -1,10 +1,10 @@ +import type SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import { IPostTestImpressionsBulk } from '@splitsoftware/splitio-commons/src/services/types'; import { IImpressionCountsCacheBase, IImpressionsCacheAsync } from '@splitsoftware/splitio-commons/src/storages/types'; import { StoredImpressionWithMetadata } from '@splitsoftware/splitio-commons/src/sync/submitters/types'; import { truncateTimeFrame } from '@splitsoftware/splitio-commons/src/utils/time'; import { ImpressionObserver } from '@splitsoftware/splitio-commons/src/trackers/impressionObserver/ImpressionObserver'; import { groupBy, metadataToHeaders } from './utils'; -import type SplitIO from '@splitsoftware/splitio-commons/types/splitio'; import { ILogger } from '@splitsoftware/splitio-commons/src/logger/types'; import { IMetadata } from '@splitsoftware/splitio-commons/src/dtos/types'; import { ImpressionsPayload } from '@splitsoftware/splitio-commons/src/sync/submitters/types'; @@ -48,6 +48,7 @@ export const impressionWithMetadataToImpressionDTO = (storedImpression: StoredIm changeNumber: i.c, time: i.m, pt: i.pt, + properties: i.properties, } as SplitIO.ImpressionDTO, }; }; @@ -130,6 +131,7 @@ export function impressionsSubmitterFactory( r: entry.label, // Rule label c: entry.changeNumber, // ChangeNumber pt: entry.pt, // Previous time + properties: entry.properties, }; }), }); diff --git a/src/synchronizers/SplitsSynchronizer.ts b/src/synchronizers/SplitsSynchronizer.ts index d2ff370..eba3621 100644 --- a/src/synchronizers/SplitsSynchronizer.ts +++ b/src/synchronizers/SplitsSynchronizer.ts @@ -1,9 +1,10 @@ import { IFetchSplitChanges } from '@splitsoftware/splitio-commons/src/services/types'; +import { InMemoryStorageFactory } from '@splitsoftware/splitio-commons/src/storages/inMemory/InMemoryStorage'; import { splitChangesFetcherFactory } from '@splitsoftware/splitio-commons/src/sync/polling/fetchers/splitChangesFetcher'; import { splitChangesUpdaterFactory } from '@splitsoftware/splitio-commons/src/sync/polling/updaters/splitChangesUpdater'; import { ISettings } from '@splitsoftware/splitio-commons/src/types'; import { ISplit } from '@splitsoftware/splitio-commons/src/dtos/types'; -import { ISegmentsCacheAsync, ISplitsCacheAsync, IStorageSync } from '@splitsoftware/splitio-commons/src/storages/types'; +import { IStorageAsync, IStorageSync } from '@splitsoftware/splitio-commons/src/storages/types'; type ISplitChangesUpdater = (noCache?: boolean) => Promise; @@ -12,13 +13,9 @@ type ISplitChangesUpdater = (noCache?: boolean) => Promise; */ export class SplitsSynchronizer { /** - * The local reference to the Synchronizer's Split Storage. + * The local reference to the Synchronizer's Storage. */ - private _segmentsStorage; - /** - * The local reference to the Synchronizer's Segments Storage. - */ - private _splitsStorage; + private _storage; /** * The local reference to the Fetch implementation required to perform requests. */ @@ -48,26 +45,19 @@ export class SplitsSynchronizer { /** * @param {IFetchSplitChanges} splitFetcher The SplitChanges fetcher from Split API. * @param {ISettings} settings The Synchronizer's settings. - * @param {ISplitsCacheAsync} splitsStorage The reference to the current Split Storage. - * @param {ISegmentsCacheAsync} segmentsStorage The reference to the current Cache Storage. - * @param {IStorageSync} inMemoryStorage The reference to the current Cache Storage. - * @param {IStorageSync} inMemoryStorageSnapshot The reference to the current Cache Storage. + * @param {IStorageAsync} storage The reference to the current Storage. */ constructor( splitFetcher: IFetchSplitChanges, settings: ISettings, - splitsStorage: ISplitsCacheAsync, - segmentsStorage: ISegmentsCacheAsync, - inMemoryStorage: IStorageSync, - inMemoryStorageSnapshot: IStorageSync, + storage: IStorageAsync, ) { - this._splitsStorage = splitsStorage; - this._segmentsStorage = segmentsStorage; + this._storage = storage; this._settings = settings; this._fetcher = splitChangesFetcherFactory(splitFetcher); - this._splitUpdater = undefined; - this._inMemoryStorage = inMemoryStorage; - this._inMemoryStorageSnapshot = inMemoryStorageSnapshot; + this._splitUpdater = undefined; // @ts-ignore + this._inMemoryStorage = InMemoryStorageFactory({ settings }); // @ts-ignore + this._inMemoryStorageSnapshot = InMemoryStorageFactory({ settings }); } /** @@ -79,8 +69,7 @@ export class SplitsSynchronizer { this._splitUpdater = splitChangesUpdaterFactory( this._settings.log, this._fetcher, - this._splitsStorage, - this._segmentsStorage, + this._storage, this._settings.sync.__splitFiltersValidation, ); return this._splitUpdater(); @@ -91,28 +80,19 @@ export class SplitsSynchronizer { * @param {SplitsCacheInMemory} splitCacheInMemory Reference to the local InMemoryCache. */ async getDataFromStorage() { - const _splitsList: [string, ISplit][] = []; try { - const splits = await this._splitsStorage.getAll(); - - splits.forEach((split) => { - _splitsList.push([split.name, split]); - }); + const splits = await this._storage.splits.getAll(); - this._inMemoryStorage.splits.addSplits(_splitsList); + const changeNumber = await this._storage.splits.getChangeNumber(); + this._inMemoryStorage.splits.update(splits, [], changeNumber); - this._inMemoryStorageSnapshot.splits.addSplits(_splitsList); + this._inMemoryStorageSnapshot.splits.update(splits, [], changeNumber); - const registeredSegments = await this._segmentsStorage.getRegisteredSegments(); + const registeredSegments = await this._storage.segments.getRegisteredSegments(); if (registeredSegments.length > 0) { this._inMemoryStorage.segments.registerSegments(registeredSegments); this._inMemoryStorageSnapshot.segments.registerSegments(registeredSegments); } - const changeNumber = await this._splitsStorage.getChangeNumber(); - - this._inMemoryStorage.splits.setChangeNumber(changeNumber); - this._inMemoryStorageSnapshot.splits.setChangeNumber(changeNumber); - } catch (error) { this._settings.log.error( `Feature flags InMemory synchronization: Error when retreving data from external Storage. Error: ${error}` @@ -137,7 +117,7 @@ export class SplitsSynchronizer { if (splits.length > 0) { - const splitsToStore: [string, ISplit][] = []; + const splitsToStore: ISplit[] = []; for (let i = 0; i < splits.length; i++) { const split = splits[i]; const { name, changeNumber } = split; @@ -146,26 +126,25 @@ export class SplitsSynchronizer { if (split) { // If the feature flag doesn't exists. if (!oldSplitDefinition) { - splitsToStore.push([name, split]); + splitsToStore.push(split); continue; } // If the feature flag exists and needs to be updated. if (oldSplitDefinition.changeNumber !== changeNumber) { - splitsToStore.push([name, split]); + splitsToStore.push(split); continue; } } } - await this._splitsStorage.addSplits(splitsToStore); + await this._storage.splits.update(splitsToStore, [], changeNumber); } - await this._splitsStorage.setChangeNumber(changeNumber); const registeredSegments = this._inMemoryStorage.segments.getRegisteredSegments(); // @todo: Update segment definitions and change number if (registeredSegments.length > 0) - await this._segmentsStorage.registerSegments(registeredSegments); + await this._storage.segments.registerSegments(registeredSegments); } catch (error) { this._settings.log.error( `Feature flags InMemory synchronization: Error when storing data to external Storage. Error: ${error}` @@ -183,8 +162,7 @@ export class SplitsSynchronizer { this._splitUpdater = splitChangesUpdaterFactory( this._settings.log, this._fetcher, - this._inMemoryStorage.splits, - this._inMemoryStorage.segments, + this._inMemoryStorage, this._settings.sync.__splitFiltersValidation, ); try { @@ -223,7 +201,8 @@ export class SplitsSynchronizer { } }); - await this._splitsStorage.removeSplits(splitKeysToRemove); + // @ts-expect-error + await this._storage.splits.update([], splitKeysToRemove.map((k) => ({ name: k })), this._storage.splits.getChangeNumber()); return deletedAmount; } diff --git a/src/synchronizers/__tests__/SplitsSynchronizer.spec.ts b/src/synchronizers/__tests__/SplitsSynchronizer.spec.ts index b07a85e..ca56aec 100644 --- a/src/synchronizers/__tests__/SplitsSynchronizer.spec.ts +++ b/src/synchronizers/__tests__/SplitsSynchronizer.spec.ts @@ -1,4 +1,5 @@ // @ts-nocheck +import { ISplit } from '@splitsoftware/splitio-commons/src/dtos/types'; import { SplitsSynchronizer } from '../SplitsSynchronizer'; describe('Splits Synchronizer', () => { @@ -7,59 +8,29 @@ describe('Splits Synchronizer', () => { status: 200, json: Promise.resolve, })); - const _settingsMock = { log: { error: () => {} } }; - - const _splitsInMemoryCacheFactory = () => { - const _splitsCache = []; - let _changeNumber; - - return { - addSplits: jest.fn((splitList: [string, string][]) => { - splitList.forEach((keyValueTuple) => { - _splitsCache.push(keyValueTuple[1]); - }); - }), - getSplitNames: jest.fn(() => _splitsCache.map(s => JSON.parse(s).name)), - getSplit: jest.fn((name) => { - const res = _splitsCache.find(i => i[0] === name); - return res ? res[1] : null; - }), - setChangeNumber: jest.fn((number) => _changeNumber = number), - getChangeNumber: jest.fn(() => _changeNumber), - getAll: jest.fn(() => _splitsCache), - removeSplits: jest.fn((names) => { - for (let i = 0; i < names.length; i++) { - const index = _splitsCache.indexOf(names[i]); - if (index) _splitsCache.splice(index, 1); - } - }), - }; - }; - - const _segmentsInMemoryCacheFactory = () => { - let _segmentsCache = []; - - return { - registerSegments: jest.fn((segments) => segments.map(s => _segmentsCache.push(s))), - getRegisteredSegments: jest.fn(() => _segmentsCache), - }; + const _settingsMock = { + log: { error: () => { } }, + scheduler: { impressionsQueueSize: 100 }, + sync: { __splitFiltersValidation: false }, + core: { key: undefined }, }; const _splitStorageMock = (() => { const _splitsStored = [ - JSON.stringify({ name: 'pepito1', changeNumber: 0 }), - JSON.stringify({ name: 'pepito2', changeNumber: 0 }), + { name: 'pepito1', changeNumber: 0 }, + { name: 'pepito2', changeNumber: 0 }, ]; let _changeNumber = 1; return { - addSplits: jest.fn((tupleList: [string, string][]) => tupleList.map(tuple => _splitsStored.push(tuple[1]))), - getSplitNames: jest.fn(() => Promise.resolve(_splitsStored.map(s => JSON.parse(s).name))), + update: jest.fn((toAdd: ISplit[], toRemove, n) => { + toAdd.map(s => _splitsStored.push(s)); + _changeNumber = n; + }), + getSplitNames: jest.fn(() => Promise.resolve(_splitsStored.map(s => s.name))), getAll: jest.fn(() => _splitsStored), - getSplit: jest.fn((name) => Promise.resolve(_splitsStored[name])), - setChangeNumber: jest.fn((n) => _changeNumber = n), + getSplit: jest.fn((name) => Promise.resolve(_splitsStored.find(s => s.name === name))), getChangeNumber: jest.fn(() => _changeNumber), - removeSplits: jest.fn(() => Promise.resolve), }; })(); @@ -68,29 +39,22 @@ describe('Splits Synchronizer', () => { return { registerSegments: jest.fn((segments) => { segments.map(s => _registeredSegments.push(s)); - _registeredSegments = Array.from(new Set(_registeredSegments)); + _registeredSegments = Array.from(new Set(_registeredSegments)); }), getRegisteredSegments: jest.fn(() => _registeredSegments), }; })(); - const _inMemoryStorageMockFactory = () => { - return { - splits: _splitsInMemoryCacheFactory(), - segments: _segmentsInMemoryCacheFactory(), - }; - }; - let _splitsSynchronizer; const createNewSynchronizer = () => { _splitsSynchronizer = new SplitsSynchronizer( _splitFetcherMock, _settingsMock, - _splitStorageMock, - _segmentsStorageMock, - _inMemoryStorageMockFactory(), - _inMemoryStorageMockFactory() + { + splits: _splitStorageMock, + segments: _segmentsStorageMock, + } ); }; @@ -104,7 +68,7 @@ describe('Splits Synchronizer', () => { test('retrieves [SPLITS] stored from Storage into InMemory cache', () => { const splitsNames = _splitsSynchronizer._inMemoryStorage.splits .getAll() - .map((split) => JSON.parse(split).name); + .map((split) => split.name); expect(splitsNames).toEqual(['pepito1', 'pepito2']); expect(_splitsSynchronizer._inMemoryStorage.splits.getChangeNumber()).toBe(1); @@ -118,18 +82,10 @@ describe('Splits Synchronizer', () => { describe('Process Differeces between data previous to Sync and new data.', () => { beforeAll(() => { createNewSynchronizer(); - _splitsSynchronizer._inMemoryStorageSnapshot.splits.addSplits([ - ['pepito1', '{"name":"pepito1","changeNumber":4}'], - ]); - _splitsSynchronizer._inMemoryStorageSnapshot.splits.addSplits([ - ['pepito2', '{"name":"pepito2","changeNumber":4}'], - ]); - _splitsSynchronizer._inMemoryStorage.splits.addSplits([ - ['pepito2', '{"name":"pepito2","changeNumber":5}'], - ]); - _splitsSynchronizer._inMemoryStorage.splits.addSplits([ - ['pepito3', '{"name":"pepito3","changeNumber":3}'], - ]); + _splitsSynchronizer._inMemoryStorageSnapshot.splits.update([{ name: 'pepito1', changeNumber: 4 }], [], 4); + _splitsSynchronizer._inMemoryStorageSnapshot.splits.update([{ name: 'pepito2', changeNumber: 4 }], [], 4); + _splitsSynchronizer._inMemoryStorage.splits.update([{ name: 'pepito2', changeNumber: 5 }], [], 5); + _splitsSynchronizer._inMemoryStorage.splits.update([{ name: 'pepito3', changeNumber: 3 }], [], 3); }); test('compares the updated InMemory cache with previous snapshot and removes [1] unusued split', async () => { @@ -143,11 +99,8 @@ describe('Splits Synchronizer', () => { beforeAll(async () => { createNewSynchronizer(); // adding some data simulating synchronization execution') - _splitsSynchronizer._inMemoryStorage.splits.addSplits([ - ['pepito3', JSON.stringify({ name: 'pepito3', changeNumber: 6 })], - ]); + _splitsSynchronizer._inMemoryStorage.splits.update([{ name: 'pepito3', changeNumber: 6 }], [], 12); - _splitsSynchronizer._inMemoryStorage.splits.setChangeNumber(12); _splitsSynchronizer._inMemoryStorage.segments.registerSegments(['anotherSegment']); await _splitsSynchronizer.putDataToStorage(); From df307342998dd2b6c5930ed99feb31c8de383fd1 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 15 May 2025 10:15:27 -0300 Subject: [PATCH 03/12] Update changelog entry with breaking changes --- CHANGES.txt | 3 +++ README.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 9ca9159..b208541 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,9 @@ 1.0.0 (May XXX, 2025) - Updated @splitsoftware/splitio-commons package to version 2.2.0. - Updated some transitive dependencies for vulnerability fixes. + - BREAKING CHANGES: + - Dropped support for Node.js v8. The SDK now requires Node.js v14 or above. + - Removed internal ponyfills for the `Map` and `Set` global objects. The SDK now requires the runtime environment to support these features natively or provide a polyfill. 0.7.0 (August 5, 2024) - Added `sync.requestOptions.agent` option to allow passing a custom Node.js HTTP(S) Agent with specific configurations for the Synchronizer requests, like custom TLS settings or a network proxy (See https://help.split.io/hc/en-us/articles/4421513571469-Split-JavaScript-synchronizer-tools#proxy). diff --git a/README.md b/README.md index e1f05a5..017a304 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This package includes a set of JavaScript synchronization tools built based on t [![Twitter Follow](https://img.shields.io/twitter/follow/splitsoftware.svg?style=social&label=Follow&maxAge=1529000)](https://twitter.com/intent/follow?screen_name=splitsoftware) ## Compatibility -Split sync tools supports Node.js version 8 or higher. To run the tools in other JavaScript environments, the target environment must support ES6 (ECMAScript 2015) syntax, and provide built-in support or a global polyfill for Promises and Web Fetch API. +Split sync tools supports Node.js version 14 or higher. To run the tools in other JavaScript environments, the target environment must support ES6 (ECMAScript 2015) syntax, and provide built-in support or a global polyfill for Promises, Web Fetch API, Map and Set. ## Getting started Below is a simple example that describes the execution of the JavaScript Synchronizer: From 3128b5b81dbb3e512bf7eae8f2a5e802af04b923 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 15 May 2025 19:03:32 -0300 Subject: [PATCH 04/12] Update changelog entry and type definitions for JS-commons 2.2.0 --- CHANGES.txt | 7 ++++--- types/index.d.ts | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b208541..d386fe2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,13 +1,14 @@ 1.0.0 (May XXX, 2025) - - Updated @splitsoftware/splitio-commons package to version 2.2.0. - - Updated some transitive dependencies for vulnerability fixes. + - Added support for synchronizing SDK impressions with properties. + - Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance Synchronizer HTTP request Headers for Authorization Frameworks. + - Updated @splitsoftware/splitio-commons package to version 2.2.0 and some transitive dependencies for vulnerability fixes and other improvements. - BREAKING CHANGES: - Dropped support for Node.js v8. The SDK now requires Node.js v14 or above. - Removed internal ponyfills for the `Map` and `Set` global objects. The SDK now requires the runtime environment to support these features natively or provide a polyfill. 0.7.0 (August 5, 2024) - Added `sync.requestOptions.agent` option to allow passing a custom Node.js HTTP(S) Agent with specific configurations for the Synchronizer requests, like custom TLS settings or a network proxy (See https://help.split.io/hc/en-us/articles/4421513571469-Split-JavaScript-synchronizer-tools#proxy). - - Updated some transitive dependencies for vulnerability fixes. + - Updated @splitsoftware/splitio-commons package to version 1.16.0 and some transitive dependencies for vulnerability fixes. 0.6.0 (May 13, 2024) - Added a new configuration option `sync.flagSpecVersion` to specify the flags spec version of feature flag definitions to be fetched and stored. diff --git a/types/index.d.ts b/types/index.d.ts index 6a5275e..b808585 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -193,6 +193,28 @@ declare module JsSyncTools { * @see {@link https://www.npmjs.com/package/node-fetch#options} */ requestOptions?: { + /** + * Custom function called before each request, allowing you to add or update headers in Synchronizer HTTP requests. + * Some headers, such as `SplitSDKVersion`, are required by the Synchronizer and cannot be overridden. + * To pass multiple headers with the same name, combine their values into a single line, separated by commas. Example: `{ 'Authorization': 'value1, value2' }` + * Or provide keys with different cases since headers are case-insensitive. Example: `{ 'authorization': 'value1', 'Authorization': 'value2' }` + * + * @defaultValue `undefined` + * + * @param context - The context for the request, which contains the `headers` property object representing the current headers in the request. + * @returns An object representing a set of headers to be merged with the current headers. + * + * @example + * ``` + * const getHeaderOverrides = (context) => { + * return { + * 'Authorization': context.headers['Authorization'] + ', other-value', + * 'custom-header': 'custom-value' + * }; + * }; + * ``` + */ + getHeaderOverrides?: (context: { headers: Record }) => Record; /** * Custom Node.js HTTP(S) Agent used for HTTP(S) requests. * From 70691431ded3cf5a6cb13bdc75a072b9c1e85784 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 15 May 2025 19:11:21 -0300 Subject: [PATCH 05/12] Upgrade JS-commons to use flag spec 1.3 --- CHANGES.txt | 3 +- e2e/synchronizer.test.ts | 18 +- e2e/utils/responseMocks.json | 1313 ++++++++++++----------- package-lock.json | 14 +- package.json | 2 +- src/settings/__tests__/index.spec.ts | 2 +- src/settings/defaults.ts | 5 - src/settings/index.ts | 5 +- src/synchronizers/SplitsSynchronizer.ts | 2 +- types/index.d.ts | 6 +- 10 files changed, 692 insertions(+), 678 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d386fe2..30ccbb2 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,8 @@ 1.0.0 (May XXX, 2025) + - Added support for synchronizing feature flags with prerequisites and targeting rules based on rule-based segments, by updating the default flag spec version to 1.3. If using the Split Proxy, flag spec 1.3 requires Split Proxy v5.11.0 or higher. If an older version is used, the Synchronizer will fallback to the previous flag spec version (1.2). - Added support for synchronizing SDK impressions with properties. - Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance Synchronizer HTTP request Headers for Authorization Frameworks. - - Updated @splitsoftware/splitio-commons package to version 2.2.0 and some transitive dependencies for vulnerability fixes and other improvements. + - Updated @splitsoftware/splitio-commons package to version 2.3.0 and some transitive dependencies for vulnerability fixes and other improvements. - BREAKING CHANGES: - Dropped support for Node.js v8. The SDK now requires Node.js v14 or above. - Removed internal ponyfills for the `Map` and `Set` global objects. The SDK now requires the runtime environment to support these features natively or provide a polyfill. diff --git a/e2e/synchronizer.test.ts b/e2e/synchronizer.test.ts index 4bab15a..14329ab 100644 --- a/e2e/synchronizer.test.ts +++ b/e2e/synchronizer.test.ts @@ -66,7 +66,7 @@ describe('Synchronizer e2e tests', () => { describe('Runs Synchronizer for the [FIRST] time, and', () => { beforeAll(async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=-1', { status: 200, body: responseMocks.splitChanges[0] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: responseMocks.splitChanges[0] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=-1', { status: 200, body: responseMocks.segmentChanges[0] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/ENDIOS_PEREZ?since=-1', { status: 200, body: responseMocks.segmentChanges[1] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=-1', { status: 200, body: responseMocks.segmentChanges[2] }); @@ -144,7 +144,7 @@ describe('Synchronizer e2e tests', () => { describe('Runs Synchronizer a [SECOND] time and', () => { beforeAll(async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346271', { status: 200, body: responseMocks.splitChanges[2] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346271&rbSince=-1', { status: 200, body: responseMocks.splitChanges[2] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] }); @@ -224,7 +224,7 @@ describe('Synchronizer e2e tests', () => { }); test('Run Synchronizer and check that data was popped from Redis and sent to Split BE', async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346272', { status: 200, body: responseMocks.splitChanges[3] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=-1', { status: 200, body: responseMocks.splitChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] }); @@ -272,7 +272,7 @@ describe('Synchronizer e2e tests', () => { }); test('Run Synchronizer and check that data was popped from Redis and sent to Split BE', async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346272', { status: 200, body: responseMocks.splitChanges[3] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=-1', { status: 200, body: responseMocks.splitChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] }); @@ -347,7 +347,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter describe('Synchronizer runs the first time', () => { beforeAll(async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=-1&sets=set_b', { status: 200, body: responseMocks.splitChanges[0] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_b', { status: 200, body: responseMocks.splitChanges[0] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=-1', { status: 200, body: responseMocks.segmentChanges[0] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); @@ -402,7 +402,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter describe('Synchronizer runs a second time, and', () => { beforeAll(async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346271&sets=set_b', { status: 200, body: responseMocks.splitChanges[2] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346271&rbSince=-1&sets=set_b', { status: 200, body: responseMocks.splitChanges[2] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); await _synchronizer.execute(); @@ -460,7 +460,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter }, }); - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=1619720346272&sets=set_b', { status: 500 }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=-1&sets=set_b', { status: 500 }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); expect(await synchronizer.execute()).toBe(false); @@ -477,7 +477,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter }, }); - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=-1&sets=set_b', { status: 500 }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=-1&rbSince=-1&sets=set_b', { status: 500 }); expect(await synchronizer.execute()).toBe(false); expect(keys.length).toBeGreaterThan(0); @@ -491,7 +491,7 @@ describe('Synchronizer - only Splits & Segments mode', () => { let executeImpressionsAndEventsCallSpy: jest.SpyInstance; beforeAll(async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.1&since=-1', { status: 200, body: responseMocks.splitChanges[0] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=-1&rbSince=-1', { status: 200, body: responseMocks.splitChanges[0] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=-1', { status: 200, body: responseMocks.segmentChanges[0] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=-1', { status: 200, body: responseMocks.segmentChanges[2] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); diff --git a/e2e/utils/responseMocks.json b/e2e/utils/responseMocks.json index f7e354d..925d28f 100644 --- a/e2e/utils/responseMocks.json +++ b/e2e/utils/responseMocks.json @@ -1,692 +1,709 @@ { "splitChanges": [ { - "splits": [ - { - "trafficTypeName": "testTT", - "name": "MATIAS_TEST", - "trafficAllocation": 92, - "trafficAllocationSeed": 59120715, - "seed": -2094556730, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1619720346270, - "algo": 2, - "configurations": {}, - "sets": ["set_a"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "account", - "attribute": "test" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": "/matias/i" - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + "ff": { + "d": [ + { + "trafficTypeName": "testTT", + "name": "MATIAS_TEST", + "trafficAllocation": 92, + "trafficAllocationSeed": 59120715, + "seed": -2094556730, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1619720346270, + "algo": 2, + "configurations": {}, + "sets": [ + "set_a" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "test" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "/matias/i" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "test matches /matias/i" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "account", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "test matches /matias/i" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "TEST_MATIAS", - "trafficAllocation": 44, - "trafficAllocationSeed": -1207740278, - "seed": 203792729, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1619205925116, - "algo": 2, - "configurations": {}, - "sets": ["set_b"], - "conditions": [ - { - "conditionType": "WHITELIST", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "test_maldo" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ], - "label": "whitelisted segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - }, - { - "treatment": "off", - "size": 0 - }, - { - "treatment": "PITY_MARTINEZ", - "size": 0 - }, - { - "treatment": "JUANFER_QUINTERO", - "size": 0 - }, - { - "treatment": "LUQUITAS_PRATTO", - "size": 0 - }, - { - "treatment": "ENZO_PEREZ", - "size": 0 - }, - { - "treatment": "REDO", - "size": 0 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "TEST_DOC", - "trafficAllocation": 100, - "trafficAllocationSeed": -1845986406, - "seed": 255141922, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1555536480284, - "algo": 2, - "configurations": { - "on": "{\"ojoijoii\":\"oijoijioj\",\"\":\"\"}" + ], + "label": "default rule" + } + ] }, - "sets": ["set_a", "set_b"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "desded", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + { + "trafficTypeName": "user", + "name": "TEST_MATIAS", + "trafficAllocation": 44, + "trafficAllocationSeed": -1207740278, + "seed": 203792729, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1619205925116, + "algo": 2, + "configurations": {}, + "sets": [ + "set_b" + ], + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "test_maldo" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "Lucas_Split", - "trafficAllocation": 32, - "trafficAllocationSeed": 2048379668, - "seed": 871802730, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "v1", - "changeNumber": 1619205566698, - "algo": 2, - "configurations": {}, - "sets": [], - "conditions": [ - { - "conditionType": "WHITELIST", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "Lucas_Segments_Tests" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "PITY_MARTINEZ", + "size": 0 + }, + { + "treatment": "JUANFER_QUINTERO", + "size": 0 + }, + { + "treatment": "LUQUITAS_PRATTO", + "size": 0 + }, + { + "treatment": "ENZO_PEREZ", + "size": 0 + }, + { + "treatment": "REDO", + "size": 0 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ], - "label": "whitelisted segment" + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "TEST_DOC", + "trafficAllocation": 100, + "trafficAllocationSeed": -1845986406, + "seed": 255141922, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1555536480284, + "algo": 2, + "configurations": { + "on": "{\"ojoijoii\":\"oijoijioj\",\"\":\"\"}" }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "Lucas_Segments_Tests" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 + "sets": [ + "set_a", + "set_b" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "desded", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 0 + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "Lucas_Split", + "trafficAllocation": 32, + "trafficAllocationSeed": 2048379668, + "seed": 871802730, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "v1", + "changeNumber": 1619205566698, + "algo": 2, + "configurations": {}, + "sets": [], + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Lucas_Segments_Tests" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "v1", - "size": 100 - } - ], - "label": "in segment Lucas_Segments_Tests" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Lucas_Segments_Tests" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "v1", + "size": 100 + } + ], + "label": "in segment Lucas_Segments_Tests" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "v1", - "size": 0 - } - ], - "label": "default rule" - } - ] - } - ], - "since": -1, - "till": 1619720346271 + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "v1", + "size": 0 + } + ], + "label": "default rule" + } + ] + } + ], + "s": -1, + "t": 1619720346271 + } }, { - "splits": [], - "since": 1619720346271, - "till": 1619720346271 + "ff": { + "d": [], + "s": 1619720346271, + "t": 1619720346271 + } }, { - "splits": [ - { - "trafficTypeName": "account", - "name": "TEST_RULO", - "trafficAllocation": 100, - "trafficAllocationSeed": -1845986406, - "seed": 255141922, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1555536480284, - "algo": 2, - "configurations": { - "on": "{\"ojoijoii\":\"oijoijioj\",\"\":\"\"}" - }, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "desded", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null - } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 + "ff": { + "d": [ + { + "trafficTypeName": "account", + "name": "TEST_RULO", + "trafficAllocation": 100, + "trafficAllocationSeed": -1845986406, + "seed": 255141922, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1555536480284, + "algo": 2, + "configurations": { + "on": "{\"ojoijoii\":\"oijoijioj\",\"\":\"\"}" + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "desded", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "account", - "name": "MATIAS_TEST", - "trafficAllocation": 92, - "trafficAllocationSeed": 59120715, - "seed": -2094556730, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "off", - "changeNumber": 1619720346272, - "algo": 2, - "configurations": {}, - "sets": ["set_c"], - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "account", - "attribute": "test" - }, - "matcherType": "MATCHES_STRING", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": "/matias/i" + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "account", + "name": "MATIAS_TEST", + "trafficAllocation": 92, + "trafficAllocationSeed": 59120715, + "seed": -2094556730, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1619720346272, + "algo": 2, + "configurations": {}, + "sets": [ + "set_c" + ], + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": "test" + }, + "matcherType": "MATCHES_STRING", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": "/matias/i" + } + ] }, - { - "treatment": "off", - "size": 0 - } - ], - "label": "test matches /matias/i" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "account", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 } - ] + ], + "label": "test matches /matias/i" }, - "partitions": [ - { - "treatment": "on", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "account", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "TEST_DOC", - "trafficAllocation": 100, - "trafficAllocationSeed": -1845986406, - "seed": 255141922, - "status": "ACTIVE", - "killed": false, - "defaultTreatment": "on", - "changeNumber": 1619720346272, - "algo": 2, - "configurations": { - "on": "{\"ojoijoii\":\"oijoijioj\",\"\":\"\"}" - }, - "conditions": [ - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "desded", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 0 + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "TEST_DOC", + "trafficAllocation": 100, + "trafficAllocationSeed": -1845986406, + "seed": 255141922, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "on", + "changeNumber": 1619720346272, + "algo": 2, + "configurations": { + "on": "{\"ojoijoii\":\"oijoijioj\",\"\":\"\"}" + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "desded", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "off", - "size": 100 - } - ], - "label": "default rule" - } - ] - }, - { - "trafficTypeName": "user", - "name": "Lucas_Split", - "trafficAllocation": 32, - "trafficAllocationSeed": 2048379668, - "seed": 871802730, - "status": "ARCHIVED", - "killed": false, - "defaultTreatment": "v1", - "changeNumber": 1619720346272, - "algo": 2, - "configurations": {}, - "conditions": [ - { - "conditionType": "WHITELIST", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": null, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "Lucas_Segments_Tests" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 } - ] - }, - "partitions": [ - { - "treatment": "on", - "size": 100 - } - ], - "label": "whitelisted segment" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "IN_SEGMENT", - "negate": false, - "userDefinedSegmentMatcherData": { - "segmentName": "Lucas_Segments_Tests" - }, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "Lucas_Split", + "trafficAllocation": 32, + "trafficAllocationSeed": 2048379668, + "seed": 871802730, + "status": "ARCHIVED", + "killed": false, + "defaultTreatment": "v1", + "changeNumber": 1619720346272, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Lucas_Segments_Tests" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 } - ] + ], + "label": "whitelisted segment" }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 0 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "Lucas_Segments_Tests" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "v1", - "size": 100 - } - ], - "label": "in segment Lucas_Segments_Tests" - }, - { - "conditionType": "ROLLOUT", - "matcherGroup": { - "combiner": "AND", - "matchers": [ - { - "keySelector": { - "trafficType": "user", - "attribute": null - }, - "matcherType": "ALL_KEYS", - "negate": false, - "userDefinedSegmentMatcherData": null, - "whitelistMatcherData": null, - "unaryNumericMatcherData": null, - "betweenMatcherData": null, - "booleanMatcherData": null, - "dependencyMatcherData": null, - "stringMatcherData": null + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 0 + }, + { + "treatment": "v1", + "size": 100 } - ] + ], + "label": "in segment Lucas_Segments_Tests" }, - "partitions": [ - { - "treatment": "on", - "size": 0 - }, - { - "treatment": "off", - "size": 100 + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] }, - { - "treatment": "v1", - "size": 0 - } - ], - "label": "default rule" - } - ] - } - ], - "since": 1619720346271, - "till": 1619720346272 + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "v1", + "size": 0 + } + ], + "label": "default rule" + } + ] + } + ], + "s": 1619720346271, + "t": 1619720346272 + } }, { - "splits": [], - "since": 1619720346272, - "till": 1619720346272 + "ff": { + "d": [], + "s": 1619720346272, + "t": 1619720346272 + } } ], "segmentChanges": [ diff --git a/package-lock.json b/package-lock.json index fe2ed06..a61d3f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.7.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.2.0", + "@splitsoftware/splitio-commons": "2.2.1-rc.5", "dotenv": "^9.0.1", "node-fetch": "^2.7.0", "yargs": "^17.0.1" @@ -1693,9 +1693,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", - "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", + "version": "2.2.1-rc.5", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.1-rc.5.tgz", + "integrity": "sha512-pG9yDnE7uohxf6ufFNu8nyqLG7ZvjxAvg7YHUCFn9/ym/0v3fKFbKXi0PY/Tkvq8FU+zRRuQNO40K8d9wWzjVw==", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -9208,9 +9208,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", - "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", + "version": "2.2.1-rc.5", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.1-rc.5.tgz", + "integrity": "sha512-pG9yDnE7uohxf6ufFNu8nyqLG7ZvjxAvg7YHUCFn9/ym/0v3fKFbKXi0PY/Tkvq8FU+zRRuQNO40K8d9wWzjVw==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" diff --git a/package.json b/package.json index 634f468..fef10b9 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "prepublishOnly": "npm run check && npm run test && npm run build" }, "dependencies": { - "@splitsoftware/splitio-commons": "2.2.0", + "@splitsoftware/splitio-commons": "2.2.1-rc.5", "dotenv": "^9.0.1", "node-fetch": "^2.7.0", "yargs": "^17.0.1" diff --git a/src/settings/__tests__/index.spec.ts b/src/settings/__tests__/index.spec.ts index 12a1ac4..19d46c2 100644 --- a/src/settings/__tests__/index.spec.ts +++ b/src/settings/__tests__/index.spec.ts @@ -26,7 +26,7 @@ describe('synchronizerSettingsValidator', () => { expect(settings.scheduler.eventsPerPost).toBe(defaults.scheduler.eventsPerPost); expect(settings.scheduler.impressionsPerPost).toBe(defaults.scheduler.impressionsPerPost); expect(settings.scheduler.maxRetries).toBe(config.scheduler!.maxRetries); - expect(settings.sync.flagSpecVersion).toBe('1.1'); + expect(settings.sync.flagSpecVersion).toBe('1.3'); expect(settings.sync.requestOptions).toBe(config.sync!.requestOptions); }); diff --git a/src/settings/defaults.ts b/src/settings/defaults.ts index 5e5428c..6a89b50 100644 --- a/src/settings/defaults.ts +++ b/src/settings/defaults.ts @@ -4,8 +4,6 @@ */ const version = '@VERSION@'; -export const FLAG_SPEC_VERSION = '1.1'; - /** * Default values to create settings for the JavaScript Synchronizer. */ @@ -30,9 +28,6 @@ export const defaults = { // Number of retry attempts for posting impressions and events. maxRetries: 3, }, - sync: { - flagSpecVersion: FLAG_SPEC_VERSION, - }, version: `synctoolsjs-${version}`, streamingEnabled: false, }; diff --git a/src/settings/index.ts b/src/settings/index.ts index fbe533d..7fb4f36 100644 --- a/src/settings/index.ts +++ b/src/settings/index.ts @@ -1,12 +1,13 @@ import { ILogger } from '@splitsoftware/splitio-commons/src/logger/types'; import { ISettings } from '@splitsoftware/splitio-commons/src/types'; +import { FLAG_SPEC_VERSION } from '@splitsoftware/splitio-commons/src/utils/constants'; import { isIntegerNumber } from '@splitsoftware/splitio-commons/src/utils/lang'; import { settingsValidation } from '@splitsoftware/splitio-commons/src/utils/settingsValidation/index'; import { validateLogger } from '@splitsoftware/splitio-commons/src/utils/settingsValidation/logger/builtinLogger'; import { ISynchronizerSettings } from '../../types'; -import { defaults, FLAG_SPEC_VERSION } from './defaults'; +import { defaults } from './defaults'; -const FLAG_SPEC_VERSIONS = ['1.0', FLAG_SPEC_VERSION]; +const FLAG_SPEC_VERSIONS = ['1.0', '1.1', '1.2', FLAG_SPEC_VERSION]; /** * Object with some default values to instantiate the application and fullfil internal diff --git a/src/synchronizers/SplitsSynchronizer.ts b/src/synchronizers/SplitsSynchronizer.ts index e12bbb6..36dd311 100644 --- a/src/synchronizers/SplitsSynchronizer.ts +++ b/src/synchronizers/SplitsSynchronizer.ts @@ -54,7 +54,7 @@ export class SplitsSynchronizer { ) { this._storage = storage; this._settings = settings; - this._fetcher = splitChangesFetcherFactory(splitFetcher); + this._fetcher = splitChangesFetcherFactory(splitFetcher, settings, storage); this._splitUpdater = undefined; // @ts-ignore this._inMemoryStorage = InMemoryStorageFactory({ settings }); // @ts-ignore this._inMemoryStorageSnapshot = InMemoryStorageFactory({ settings }); diff --git a/types/index.d.ts b/types/index.d.ts index b808585..f729b6b 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -171,11 +171,11 @@ declare module JsSyncTools { splitFilters?: SplitFilter[] /** * Feature Flag Spec version. Option to determine which version of the feature flag definitions are fetched and stored. - * Possible values are '1.0' and '1.1'. + * Possible values are `'1.0'`, `'1.1'`, `'1.2'`, and `'1.3'`. * - * @default '1.1' + * @default '1.3' */ - flagSpecVersion?: '1.0' | '1.1' + flagSpecVersion?: '1.0' | '1.1' | '1.2' | '1.3' /** * Impressions Collection Mode. Option to determine how impressions are going to be sent to Split Servers. * From 11b66ef51f23ffe045b189957ed6db9806eb42d0 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Fri, 16 May 2025 17:03:39 -0300 Subject: [PATCH 06/12] Update JS-commons v2.3.0 --- CHANGES.txt | 2 +- package-lock.json | 14 +++++++------- package.json | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index d386fe2..e3c1e5d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,7 +1,7 @@ 1.0.0 (May XXX, 2025) - Added support for synchronizing SDK impressions with properties. - Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance Synchronizer HTTP request Headers for Authorization Frameworks. - - Updated @splitsoftware/splitio-commons package to version 2.2.0 and some transitive dependencies for vulnerability fixes and other improvements. + - Updated @splitsoftware/splitio-commons package to version 2.3.0 and some transitive dependencies for vulnerability fixes and other improvements. - BREAKING CHANGES: - Dropped support for Node.js v8. The SDK now requires Node.js v14 or above. - Removed internal ponyfills for the `Map` and `Set` global objects. The SDK now requires the runtime environment to support these features natively or provide a polyfill. diff --git a/package-lock.json b/package-lock.json index fe2ed06..8ac4c29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.7.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.2.0", + "@splitsoftware/splitio-commons": "2.3.0", "dotenv": "^9.0.1", "node-fetch": "^2.7.0", "yargs": "^17.0.1" @@ -1693,9 +1693,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", - "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.3.0.tgz", + "integrity": "sha512-TRxutKjGw2JtlYhGYFW5T/2srF76hLDn9pNlwkHx6R0qDK7CqkjhFx/KU6LRqsibnmlh2aMl1I+TxblDncMDyA==", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -9208,9 +9208,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.2.0.tgz", - "integrity": "sha512-ywWDh2fM4/EqJ1AByjXM13gAal+z/WSGiBQ5OZmjpL/iqFLENy3yo/GwsxR/ataOi27XbRQTeQbE/eD7HVnWiA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.3.0.tgz", + "integrity": "sha512-TRxutKjGw2JtlYhGYFW5T/2srF76hLDn9pNlwkHx6R0qDK7CqkjhFx/KU6LRqsibnmlh2aMl1I+TxblDncMDyA==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" diff --git a/package.json b/package.json index 634f468..08c066e 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "prepublishOnly": "npm run check && npm run test && npm run build" }, "dependencies": { - "@splitsoftware/splitio-commons": "2.2.0", + "@splitsoftware/splitio-commons": "2.3.0", "dotenv": "^9.0.1", "node-fetch": "^2.7.0", "yargs": "^17.0.1" From d14bcfafaf734c6b3f1fce79bc384190d4cf3bad Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 20 May 2025 10:59:37 -0300 Subject: [PATCH 07/12] Apply TSDoc linter in types folder --- package.json | 2 +- types/index.d.ts | 74 ++++++++++++++---------------------------------- 2 files changed, 22 insertions(+), 54 deletions(-) diff --git a/package.json b/package.json index 08c066e..74949f5 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "license": "Apache-2.0", "scripts": { "check": "npm run check:lint && npm run check:types", - "check:lint": "eslint src --ext .js,.ts", + "check:lint": "eslint src types --ext .js,.ts", "check:types": "tsc --noEmit", "build": "npm run build:esm && npm run build:cjs", "build:esm": "rimraf lib/esm && tsc -m es2015 --outDir lib/esm --importHelpers && scripts/build_esm_replace_imports.sh && replace @VERSION@ $npm_package_version lib/esm/settings/defaults.js", diff --git a/types/index.d.ts b/types/index.d.ts index b808585..b86719c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -14,14 +14,13 @@ declare module JsSyncTools { export class Synchronizer { /** * Creates a new Synchronizer instance - * @param config The synchronizer config object + * @param config - The synchronizer config object */ constructor(config: ISynchronizerSettings); /** * Execute synchronization - * @param cb Optional error-first callback to be invoked when the synchronization ends. The callback will be invoked with an error as first argument if the synchronization fails. - * @return {Promise} A promise that resolves when the operation ends, - * with a boolean indicating if operation succeeded or not. The promise never rejects. + * @param cb - Optional error-first callback to be invoked when the synchronization ends. The callback will be invoked with an error as first argument if the synchronization fails. + * @returns A promise that resolves when the operation ends, with a boolean indicating if operation succeeded or not. The promise never rejects. */ execute(cb?: (err?: Error) => void): Promise; // @TODO expose settings eventually @@ -30,8 +29,6 @@ declare module JsSyncTools { /** * Log levels. - * - * @typedef {string} LogLevel */ type LogLevel = 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE'; /** @@ -41,30 +38,25 @@ declare module JsSyncTools { /** * String property to override the base URL where the synchronizer will get feature flagging related data like a feature flag rollout plan or segments information. * - * @property {string} sdk - * @default 'https://sdk.split.io/api' + * @defaultValue `'https://sdk.split.io/api'` */ sdk?: string /** * String property to override the base URL where the synchronizer will post impressions and events. * - * @property {string} events - * @default 'https://events.split.io/api' + * @defaultValue `'https://events.split.io/api'` */ events?: string, /** * String property to override the base URL where the synchronizer will post telemetry data. * - * @property {string} telemetry - * @default 'https://telemetry.split.io/api' + * @defaultValue `'https://telemetry.split.io/api'` */ telemetry?: string }; /** * SplitFilter type. - * - * @typedef {string} SplitFilterType */ type SplitFilterType = 'byName' | 'bySet'; /** @@ -73,73 +65,56 @@ declare module JsSyncTools { interface SplitFilter { /** * Type of the filter. - * - * @property {SplitFilterType} type */ type: SplitFilterType, /** * List of values: feature flag names for 'byName' filter type, and feature flag name prefixes for 'byPrefix' type. - * - * @property {string[]} values */ values: string[], } /** * ImpressionsMode type. - * - * @typedef {string} ImpressionsMode */ type ImpressionsMode = 'OPTIMIZED' | 'DEBUG'; /** * Settings interface for Synchronizer instances. * - * @interface ISynchronizerSettings * @see {@link https://help.split.io/hc/en-us/articles/4421513571469-Split-JavaScript-synchronizer-tools#configuration} */ interface ISynchronizerSettings { /** * Core settings. - * - * @property {Object} core */ core: { /** - * Your SDK key. More information: @see {@link https://help.split.io/hc/en-us/articles/360019916211-API-keys} + * Your SDK key. * - * @property {string} authorizationKey + * @see {@link https://help.split.io/hc/en-us/articles/360019916211-API-keys} */ authorizationKey: string } /** * Defines which kind of storage we should instantiate. - * - * @property {Object} storage */ storage: { /** * Storage type. The only possible value is "PLUGGABLE", which is the default. - * @property {'PLUGGABLE'} type */ type?: 'PLUGGABLE', /** * A valid storage instance. - * - * @property {Object} wrapper */ wrapper: Object /** * Optional prefix added to the storage keys to prevent any kind of data collision between SDK versions. * - * @property {string} prefix - * @default 'SPLITIO' + * @defaultValue `'SPLITIO'` */ prefix?: string } /** * List of URLs that the Synchronizer will use as base for it's synchronization functionalities. * Do not change these settings unless you're working an advanced use case, like connecting to a proxy. - * - * @property {Object} urls */ urls?: UrlSettings /** @@ -150,30 +125,29 @@ declare module JsSyncTools { * config.debug = true * config.debug = 'WARN' * ``` - * @property {boolean | LogLevel | ILogger} debug - * @default false + * @defaultValue `false` */ debug?: boolean | LogLevel /** * Synchronization settings. - * @property {Object} sync */ sync?: { /** * List of feature flag filters. These filters are used to fetch a subset of the feature flag definitions in your environment. * * Example: - * `splitFilter: [ - * { type: 'byName', values: ['my_feature_flag_1', 'my_feature_flag_2'] }, // will fetch feature flags named 'my_feature_flag_1' and 'my_feature_flag_2' - * ]` - * @property {SplitFilter[]} splitFilters + * ``` + * splitFilter: [ + * { type: 'byName', values: ['my_feature_flag_1', 'my_feature_flag_2'] }, // will fetch feature flags named 'my_feature_flag_1' and 'my_feature_flag_2' + * ] + * ``` */ splitFilters?: SplitFilter[] /** * Feature Flag Spec version. Option to determine which version of the feature flag definitions are fetched and stored. * Possible values are '1.0' and '1.1'. * - * @default '1.1' + * @defaultValue `'1.1'` */ flagSpecVersion?: '1.0' | '1.1' /** @@ -183,8 +157,7 @@ declare module JsSyncTools { * - 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. * - * @property {String} impressionsMode - * @default 'OPTIMIZED' + * @defaultValue `'OPTIMIZED'` */ impressionsMode?: ImpressionsMode /** @@ -237,33 +210,28 @@ declare module JsSyncTools { * * @see {@link https://nodejs.org/api/https.html#class-httpsagent} * - * @property {http.Agent | https.Agent} agent - * @default undefined + * @defaultValue `undefined` */ agent?: RequestOptions['agent'] }, } /** * Scheduler settings. - * @property {Object} scheduler */ scheduler?: { /** * Maximum number of impressions to send per POST request. - * @property {number} impressionsPerPost - * @default 1000 + * @defaultValue `1000` */ impressionsPerPost?: number /** * Maximum number of events to send per POST request. - * @property {number} eventsPerPost - * @default 1000 + * @defaultValue `1000` */ eventsPerPost?: number /** * Maximum number of retry attempts for posting impressions and events. - * @property {number} maxRetries - * @default 3 + * @defaultValue `3` */ maxRetries?: number } From 88e5440d40b7a11cbf26fdfdc17298bef724d79a Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 20 May 2025 11:19:48 -0300 Subject: [PATCH 08/12] Polish type definitions --- types/index.d.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index b86719c..2c86eab 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,6 +1,7 @@ // Type definitions for Split JavaScript Sync Tools // Project: https://www.split.io/ // Definitions by: Emiliano Sanchez + import { RequestOptions } from 'http'; export = JsSyncTools; @@ -14,11 +15,13 @@ declare module JsSyncTools { export class Synchronizer { /** * Creates a new Synchronizer instance + * * @param config - The synchronizer config object */ constructor(config: ISynchronizerSettings); /** * Execute synchronization + * * @param cb - Optional error-first callback to be invoked when the synchronization ends. The callback will be invoked with an error as first argument if the synchronization fails. * @returns A promise that resolves when the operation ends, with a boolean indicating if operation succeeded or not. The promise never rejects. */ @@ -98,7 +101,7 @@ declare module JsSyncTools { */ storage: { /** - * Storage type. The only possible value is "PLUGGABLE", which is the default. + * Storage type. The only possible value is `'PLUGGABLE'`, which is the default. */ type?: 'PLUGGABLE', /** @@ -121,10 +124,11 @@ declare module JsSyncTools { * Boolean value to indicate whether the logger should be enabled or disabled by default, or a log level string. * * Examples: - * ```typescript + * ``` * config.debug = true * config.debug = 'WARN' * ``` + * * @defaultValue `false` */ debug?: boolean | LogLevel @@ -145,7 +149,7 @@ declare module JsSyncTools { splitFilters?: SplitFilter[] /** * Feature Flag Spec version. Option to determine which version of the feature flag definitions are fetched and stored. - * Possible values are '1.0' and '1.1'. + * Possible values are `'1.0'` and `'1.1'`. * * @defaultValue `'1.1'` */ @@ -153,7 +157,7 @@ declare module JsSyncTools { /** * 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'` and `'OPTIMIZED'`. * - 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. * @@ -163,6 +167,7 @@ declare module JsSyncTools { /** * Custom options object for HTTP(S) requests in Node.js. * If provided, this object is merged with the options object passed for Node-Fetch calls. + * * @see {@link https://www.npmjs.com/package/node-fetch#options} */ requestOptions?: { @@ -193,7 +198,7 @@ declare module JsSyncTools { * * You can use it, for example, for certificate pinning or setting a network proxy: * - * ```javascript + * ``` * const { HttpsProxyAgent } = require('https-proxy-agent'); * * const proxyAgent = new HttpsProxyAgent(process.env.HTTPS_PROXY || 'http://10.10.1.10:1080'); @@ -221,16 +226,19 @@ declare module JsSyncTools { scheduler?: { /** * Maximum number of impressions to send per POST request. + * * @defaultValue `1000` */ impressionsPerPost?: number /** * Maximum number of events to send per POST request. + * * @defaultValue `1000` */ eventsPerPost?: number /** * Maximum number of retry attempts for posting impressions and events. + * * @defaultValue `3` */ maxRetries?: number From 9f49b3db99388f136ab5136a19148dfcd4632c72 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 20 May 2025 17:24:08 -0300 Subject: [PATCH 09/12] Apply TSDoc linter in e2e folder --- e2e/utils/SDKConsumerMode.ts | 3 +-- e2e/utils/redisAdapterWrapper.ts | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/e2e/utils/SDKConsumerMode.ts b/e2e/utils/SDKConsumerMode.ts index 9214d1e..fad70aa 100644 --- a/e2e/utils/SDKConsumerMode.ts +++ b/e2e/utils/SDKConsumerMode.ts @@ -23,8 +23,7 @@ const config = { * Function to run an example SDK in Consumer mode, in order to generate Events and Impressions * to be then processed by the Synchronizer. * - * @param {ImpressionsMode} impressionsMode Impressions mode. - * @returns {Promise} + * @param impressionsMode - Impressions mode. */ export default function runSDKConsumer(impressionsMode: ImpressionsMode) { const factory = SplitFactory({ diff --git a/e2e/utils/redisAdapterWrapper.ts b/e2e/utils/redisAdapterWrapper.ts index 7416dd2..c1b4f7f 100644 --- a/e2e/utils/redisAdapterWrapper.ts +++ b/e2e/utils/redisAdapterWrapper.ts @@ -7,8 +7,8 @@ import { noopLogger } from '../../src/submitters/__tests__/commonUtils'; * Creates a storage wrapper that uses our RedisAdapter. * Operations fail until `connect` is resolved once the Redis 'ready' event is emitted. * - * @param {Object} redisOptions Redis options with the format expected at `settings.storage.options`. - * @returns {IPluggableStorageWrapper} Storage wrapper instance. + * @param redisOptions - Redis options with the format expected at `settings.storage.options`. + * @returns Storage wrapper instance. */ export default function redisAdapterWrapper(redisOptions: Record): IPluggableStorageWrapper { diff --git a/package.json b/package.json index 74949f5..dee3822 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "license": "Apache-2.0", "scripts": { "check": "npm run check:lint && npm run check:types", - "check:lint": "eslint src types --ext .js,.ts", + "check:lint": "eslint src types e2e --ext .js,.ts", "check:types": "tsc --noEmit", "build": "npm run build:esm && npm run build:cjs", "build:esm": "rimraf lib/esm && tsc -m es2015 --outDir lib/esm --importHelpers && scripts/build_esm_replace_imports.sh && replace @VERSION@ $npm_package_version lib/esm/settings/defaults.js", From aa08ce9e9d6c8bb08dba495bf81ebd54846c355e Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 20 May 2025 17:55:27 -0300 Subject: [PATCH 10/12] Add tests --- e2e/synchronizer.test.ts | 18 +++++++--- e2e/utils/responseMocks.json | 45 +++++++++++++++++++++++++ src/synchronizers/SplitsSynchronizer.ts | 2 +- 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/e2e/synchronizer.test.ts b/e2e/synchronizer.test.ts index 14329ab..37e3bc9 100644 --- a/e2e/synchronizer.test.ts +++ b/e2e/synchronizer.test.ts @@ -116,6 +116,14 @@ describe('Synchronizer e2e tests', () => { expect(itemsSetB.sort()).toEqual(['TEST_DOC', 'TEST_MATIAS']); expect(itemsInexistentSet).toEqual([]); }); + + test('saves 1 rule-based segment', async () => { + const ruleBasedSegments = await _redisWrapper.getKeysByPrefix(`${REDIS_PREFIX}.rbsegment.*`); + expect(ruleBasedSegments).toHaveLength(1); + + expect(await _redisWrapper.get(`${REDIS_PREFIX}.rbsegments.till`)).toBe('100'); + }); + }); describe('Runs SDK Consumer with DEBUG impressions mode, and', () => { @@ -144,7 +152,7 @@ describe('Synchronizer e2e tests', () => { describe('Runs Synchronizer a [SECOND] time and', () => { beforeAll(async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346271&rbSince=-1', { status: 200, body: responseMocks.splitChanges[2] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346271&rbSince=100', { status: 200, body: responseMocks.splitChanges[2] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] }); @@ -224,7 +232,7 @@ describe('Synchronizer e2e tests', () => { }); test('Run Synchronizer and check that data was popped from Redis and sent to Split BE', async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=-1', { status: 200, body: responseMocks.splitChanges[3] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=100', { status: 200, body: responseMocks.splitChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] }); @@ -272,7 +280,7 @@ describe('Synchronizer e2e tests', () => { }); test('Run Synchronizer and check that data was popped from Redis and sent to Split BE', async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=-1', { status: 200, body: responseMocks.splitChanges[3] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=100', { status: 200, body: responseMocks.splitChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/Lucas_Segments_Tests?since=1617053238061', { status: 200, body: responseMocks.segmentChanges[6] }); @@ -402,7 +410,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter describe('Synchronizer runs a second time, and', () => { beforeAll(async () => { - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346271&rbSince=-1&sets=set_b', { status: 200, body: responseMocks.splitChanges[2] }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346271&rbSince=100&sets=set_b', { status: 200, body: responseMocks.splitChanges[2] }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); await _synchronizer.execute(); @@ -460,7 +468,7 @@ describe('Synchronizer e2e tests - OPTIMIZED impressions mode & Flag Sets filter }, }); - fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=-1&sets=set_b', { status: 500 }); + fetchMock.getOnce(SERVER_MOCK_URL + '/splitChanges?s=1.3&since=1619720346272&rbSince=100&sets=set_b', { status: 500 }); fetchMock.getOnce(SERVER_MOCK_URL + '/segmentChanges/test_maldo?since=1589906133231', { status: 200, body: responseMocks.segmentChanges[3] }); expect(await synchronizer.execute()).toBe(false); diff --git a/e2e/utils/responseMocks.json b/e2e/utils/responseMocks.json index 925d28f..33d51f7 100644 --- a/e2e/utils/responseMocks.json +++ b/e2e/utils/responseMocks.json @@ -371,6 +371,51 @@ ], "s": -1, "t": 1619720346271 + }, + "rbs": { + "s": -1, + "t": 100, + "d": [ + { + "changeNumber": 5, + "name": "test_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded": { + "keys": [ + "mauro@split.io", + "gaston@split.io" + ], + "segments": [ + { + "type": "standard", + "name": "test_maldo" + } + ] + }, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ] + } + ] } }, { diff --git a/src/synchronizers/SplitsSynchronizer.ts b/src/synchronizers/SplitsSynchronizer.ts index 36dd311..798f1e5 100644 --- a/src/synchronizers/SplitsSynchronizer.ts +++ b/src/synchronizers/SplitsSynchronizer.ts @@ -142,7 +142,7 @@ export class SplitsSynchronizer { const registeredSegments = this._inMemoryStorage.segments.getRegisteredSegments(); - // @todo: Update segment definitions and change number + // @todo: Update rule-based segments, segment definitions and change number if (registeredSegments.length > 0) await this._storage.segments.registerSegments(registeredSegments); } catch (error) { From 10fd80c9d33af848c71ffccded96cd5335dc3f26 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 21 May 2025 13:31:24 -0300 Subject: [PATCH 11/12] rc --- package-lock.json | 4 ++-- package.json | 2 +- src/Synchronizer.ts | 32 +++++++++++++++++--------------- src/settings/index.ts | 2 +- 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index c06b22c..f964c7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@splitsoftware/splitio-sync-tools", - "version": "0.7.0", + "version": "1.0.0-rc.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-sync-tools", - "version": "0.7.0", + "version": "1.0.0-rc.0", "license": "Apache-2.0", "dependencies": { "@splitsoftware/splitio-commons": "2.3.1-rc.0", diff --git a/package.json b/package.json index d112599..a822e2b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-sync-tools", - "version": "0.7.0", + "version": "1.0.0-rc.0", "description": "Split JavaScript Sync Tools", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", diff --git a/src/Synchronizer.ts b/src/Synchronizer.ts index b4eae52..3bed790 100644 --- a/src/Synchronizer.ts +++ b/src/Synchronizer.ts @@ -2,8 +2,10 @@ import { splitApiFactory } from '@splitsoftware/splitio-commons/src/services/spl import { ISplitApi } from '@splitsoftware/splitio-commons/src/services/types'; import { IStorageAsync, ITelemetryCacheAsync } from '@splitsoftware/splitio-commons/src/storages/types'; import { ISettings } from '@splitsoftware/splitio-commons/src/types'; -import { SegmentsSynchronizer } from './synchronizers/SegmentsSynchronizer'; -import { SplitsSynchronizer } from './synchronizers/SplitsSynchronizer'; +import { segmentChangesFetcherFactory } from '@splitsoftware/splitio-commons/src/sync/polling/fetchers/segmentChangesFetcher'; +import { segmentChangesUpdaterFactory } from '@splitsoftware/splitio-commons/src/sync/polling/updaters/segmentChangesUpdater'; +import { splitChangesFetcherFactory } from '@splitsoftware/splitio-commons/src/sync/polling/fetchers/splitChangesFetcher'; +import { splitChangesUpdaterFactory } from '@splitsoftware/splitio-commons/src/sync/polling/updaters/splitChangesUpdater'; import { synchronizerStorageFactory } from './storages/synchronizerStorage'; import { eventsSubmitterFactory } from './submitters/eventsSubmitter'; import { impressionsSubmitterFactory } from './submitters/impressionsSubmitter'; @@ -36,13 +38,13 @@ export class Synchronizer { */ private _splitApi: ISplitApi; /** - * The local reference to the SegmentsUpdater instance from `@splitio/javascript-commons`. + * The local reference to the segmentChangesUpdater instance from `@splitio/javascript-commons`. */ - private _segmentsSynchronizer!: SegmentsSynchronizer; + private _segmentChangesUpdater!: ReturnType; /** - * The local reference to the SplitUpdater instance from `@splitio/javascript-commons`. + * The local reference to the splitChangesUpdater instance from `@splitio/javascript-commons`. */ - private _splitsSynchronizer!: SplitsSynchronizer; + private _splitChangesUpdater!: ReturnType; /** * The local reference to the EventsSynchronizer class. */ @@ -137,15 +139,16 @@ export class Synchronizer { new ImpressionCountsCacheInMemory() : undefined; - this._segmentsSynchronizer = new SegmentsSynchronizer( - this._splitApi.fetchSegmentChanges, - this.settings, + this._segmentChangesUpdater = segmentChangesUpdaterFactory( + this.settings.log, + segmentChangesFetcherFactory(this._splitApi.fetchSegmentChanges), this._storage.segments, ); - this._splitsSynchronizer = new SplitsSynchronizer( - this._splitApi.fetchSplitChanges, - this.settings, + this._splitChangesUpdater = splitChangesUpdaterFactory( + this.settings.log, + splitChangesFetcherFactory(this._splitApi.fetchSplitChanges, this.settings, this._storage), this._storage, + this.settings.sync.__splitFiltersValidation ); this._eventsSubmitter = eventsSubmitterFactory( this.settings.log, @@ -260,11 +263,10 @@ export class Synchronizer { private async executeSplitsAndSegments(standalone = true) { if (standalone) await this.preExecute(); - // @TODO optimize SplitChangesUpdater to reduce storage operations ("inMemoryOperation" mode) - const isSplitsSyncSuccessful = await this._splitsSynchronizer.getSplitChanges(); + const isSplitsSyncSuccessful = await this._splitChangesUpdater(); this.settings.log.debug(`Feature flags Synchronizer task: ${isSplitsSyncSuccessful ? 'Successful' : 'Unsuccessful'}`); - const isSegmentsSyncSuccessful = await this._segmentsSynchronizer.getSegmentsChanges(); + const isSegmentsSyncSuccessful = await this._segmentChangesUpdater(); this.settings.log.debug(`Segments Synchronizer task: ${isSegmentsSyncSuccessful ? 'Successful' : 'Unsuccessful'}`); if (standalone) await this.postExecute(); diff --git a/src/settings/index.ts b/src/settings/index.ts index 7fb4f36..e590daa 100644 --- a/src/settings/index.ts +++ b/src/settings/index.ts @@ -53,7 +53,7 @@ export function synchronizerSettingsValidator( const { scheduler, log } = settings; // @TODO validate synchronizerMode eventually - // @TODO: validate minimum and maximum value for config params. + // @TODO validate minimum and maximum value for config params. scheduler.eventsPerPost = validatePositiveInteger(log, 'eventsPerPost', scheduler.eventsPerPost, defaults.scheduler.eventsPerPost); scheduler.impressionsPerPost = validatePositiveInteger(log, 'impressionsPerPost', scheduler.impressionsPerPost, defaults.scheduler.impressionsPerPost); scheduler.maxRetries = validatePositiveInteger(log, 'maxRetries', scheduler.maxRetries, defaults.scheduler.maxRetries); From ff21f473011c660ca809b81f949534b3ff355fdd Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Tue, 27 May 2025 17:47:48 -0300 Subject: [PATCH 12/12] Stable version --- CHANGES.txt | 5 +++-- package-lock.json | 18 +++++++++--------- package.json | 4 ++-- src/Synchronizer.ts | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 30ccbb2..4dbca39 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,6 @@ -1.0.0 (May XXX, 2025) - - Added support for synchronizing feature flags with prerequisites and targeting rules based on rule-based segments, by updating the default flag spec version to 1.3. If using the Split Proxy, flag spec 1.3 requires Split Proxy v5.11.0 or higher. If an older version is used, the Synchronizer will fallback to the previous flag spec version (1.2). +1.0.0 (May 28, 2025) + - Added support for synchronizing feature flags with rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK. + - Added support for synchronizing feature flags with prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules. - Added support for synchronizing SDK impressions with properties. - Added `sync.requestOptions.getHeaderOverrides` configuration option to enhance Synchronizer HTTP request Headers for Authorization Frameworks. - Updated @splitsoftware/splitio-commons package to version 2.3.0 and some transitive dependencies for vulnerability fixes and other improvements. diff --git a/package-lock.json b/package-lock.json index f964c7e..056a286 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@splitsoftware/splitio-sync-tools", - "version": "1.0.0-rc.0", + "version": "1.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@splitsoftware/splitio-sync-tools", - "version": "1.0.0-rc.0", + "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@splitsoftware/splitio-commons": "2.3.1-rc.0", + "@splitsoftware/splitio-commons": "2.4.0", "dotenv": "^9.0.1", "node-fetch": "^2.7.0", "yargs": "^17.0.1" @@ -1693,9 +1693,9 @@ } }, "node_modules/@splitsoftware/splitio-commons": { - "version": "2.3.1-rc.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.3.1-rc.0.tgz", - "integrity": "sha512-+IQdkvy9FPD/r2v40vew8wpVFMPbFWmYXaQ1uhTgytax3nLI0TokoSjEBizEfE7B6xcQ22AZ4ogAqDnIvQBryw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.4.0.tgz", + "integrity": "sha512-VjrzXe7zDM5oi+VWfNNAu1DtcsZl1he8c/MeC4O2SiNRid+Nurzs0ROziHEcBt/4nnCI7vZMNdM4FCcnZHMccA==", "license": "Apache-2.0", "dependencies": { "@types/ioredis": "^4.28.0", @@ -9208,9 +9208,9 @@ } }, "@splitsoftware/splitio-commons": { - "version": "2.3.1-rc.0", - "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.3.1-rc.0.tgz", - "integrity": "sha512-+IQdkvy9FPD/r2v40vew8wpVFMPbFWmYXaQ1uhTgytax3nLI0TokoSjEBizEfE7B6xcQ22AZ4ogAqDnIvQBryw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-2.4.0.tgz", + "integrity": "sha512-VjrzXe7zDM5oi+VWfNNAu1DtcsZl1he8c/MeC4O2SiNRid+Nurzs0ROziHEcBt/4nnCI7vZMNdM4FCcnZHMccA==", "requires": { "@types/ioredis": "^4.28.0", "tslib": "^2.3.1" diff --git a/package.json b/package.json index a822e2b..740ef25 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-sync-tools", - "version": "1.0.0-rc.0", + "version": "1.0.0", "description": "Split JavaScript Sync Tools", "main": "lib/cjs/index.js", "module": "lib/esm/index.js", @@ -50,7 +50,7 @@ "prepublishOnly": "npm run check && npm run test && npm run build" }, "dependencies": { - "@splitsoftware/splitio-commons": "2.3.1-rc.0", + "@splitsoftware/splitio-commons": "2.4.0", "dotenv": "^9.0.1", "node-fetch": "^2.7.0", "yargs": "^17.0.1" diff --git a/src/Synchronizer.ts b/src/Synchronizer.ts index 3bed790..268003c 100644 --- a/src/Synchronizer.ts +++ b/src/Synchronizer.ts @@ -194,7 +194,7 @@ export class Synchronizer { private async preExecute(): Promise { const log = this.settings.log; if (!getFetch()) throw new Error('Global Fetch API is not available'); - log.info('Synchronizer: Execute'); + log.info(`Synchronizer: Execute. Version: ${this.settings.version}`); const areAPIsReady = await this._checkEndpointHealth(); if (!areAPIsReady) throw new Error('Health check of Split API endpoints failed');