diff --git a/package-lock.json b/package-lock.json index ea2edb24..36876610 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.2.1-rc.5", + "version": "1.2.1-rc.7", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 719eb3f6..03f9ec7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@splitsoftware/splitio-commons", - "version": "1.2.1-rc.5", + "version": "1.2.1-rc.7", "description": "Split Javascript SDK common components", "main": "cjs/index.js", "module": "esm/index.js", diff --git a/src/sync/streaming/SSEClient/index.ts b/src/sync/streaming/SSEClient/index.ts index 378ac9c0..7c0dfb6e 100644 --- a/src/sync/streaming/SSEClient/index.ts +++ b/src/sync/streaming/SSEClient/index.ts @@ -1,5 +1,6 @@ import { IEventSourceConstructor } from '../../../services/types'; import { ISettings } from '../../../types'; +import { isString } from '../../../utils/lang'; import { IAuthTokenPushEnabled } from '../AuthClient/types'; import { ISSEClient, ISseEventHandler } from './types'; @@ -15,7 +16,7 @@ const CONTROL_CHANNEL_REGEX = /^control_/; */ function buildSSEHeaders(settings: ISettings) { const headers: Record = { - SplitSDKClientKey: settings.core.authorizationKey.slice(-4), + SplitSDKClientKey: isString(settings.core.authorizationKey) ? settings.core.authorizationKey.slice(-4) : '', SplitSDKVersion: settings.version, }; diff --git a/src/sync/submitters/__tests__/eventsSyncTask.spec.ts b/src/sync/submitters/__tests__/eventsSyncTask.spec.ts new file mode 100644 index 00000000..59b647c1 --- /dev/null +++ b/src/sync/submitters/__tests__/eventsSyncTask.spec.ts @@ -0,0 +1,61 @@ +import { eventsSyncTaskFactory } from '../eventsSyncTask'; +import { loggerMock } from '../../../logger/__tests__/sdkLogger.mock'; + + + +describe('Events submitter (eventsSyncTask)', () => { + + let __onFullQueueCb: () => void; + const postEventsBulkMock = jest.fn(); + const eventsCacheMock = { + isEmpty: jest.fn(() => true), + setOnFullQueueCb: jest.fn(function (onFullQueueCb) { __onFullQueueCb = onFullQueueCb; }) + }; + + beforeEach(() => { + eventsCacheMock.isEmpty.mockClear(); + }); + + test('with eventsFirstPushWindow', async () => { + const eventsFirstPushWindow = 20; // @ts-ignore + const eventsSubmitter = eventsSyncTaskFactory(loggerMock, postEventsBulkMock, eventsCacheMock, 30000, eventsFirstPushWindow); + + eventsSubmitter.start(); + expect(eventsSubmitter.isRunning()).toEqual(true); // Submitter should be flagged as running + expect(eventsSubmitter.isExecuting()).toEqual(false); // but not executed immediatelly if there is a push window + expect(eventsCacheMock.isEmpty).not.toBeCalled(); + + // If queue is full, submitter should be executed + __onFullQueueCb(); + expect(eventsSubmitter.isExecuting()).toEqual(true); + expect(eventsCacheMock.isEmpty).toBeCalledTimes(1); + + // Await first push window + await new Promise(res => setTimeout(res, eventsFirstPushWindow + 10)); + expect(eventsCacheMock.isEmpty).toBeCalledTimes(2); // after the push window, submitter should have been executed + + expect(eventsSubmitter.isRunning()).toEqual(true); + eventsSubmitter.stop(); + expect(eventsSubmitter.isRunning()).toEqual(false); + }); + + test('without eventsFirstPushWindow', async () => { + // @ts-ignore + const eventsSubmitter = eventsSyncTaskFactory(loggerMock, postEventsBulkMock, eventsCacheMock, 30000); + + eventsSubmitter.start(); + expect(eventsSubmitter.isRunning()).toEqual(true); // Submitter should be flagged as running + expect(eventsSubmitter.isExecuting()).toEqual(true); // and executes immediatelly if there isn't a push window + expect(eventsCacheMock.isEmpty).toBeCalledTimes(1); + + // If queue is full, submitter should be executed + __onFullQueueCb(); + expect(eventsSubmitter.isExecuting()).toEqual(true); + expect(eventsCacheMock.isEmpty).toBeCalledTimes(2); + + expect(eventsSubmitter.isRunning()).toEqual(true); + eventsSubmitter.stop(); + expect(eventsSubmitter.isRunning()).toEqual(false); + }); + +}); diff --git a/src/sync/submitters/eventsSyncTask.ts b/src/sync/submitters/eventsSyncTask.ts index b87ee24e..7c84374b 100644 --- a/src/sync/submitters/eventsSyncTask.ts +++ b/src/sync/submitters/eventsSyncTask.ts @@ -22,18 +22,25 @@ export function eventsSyncTaskFactory( // don't retry events. const syncTask = submitterSyncTaskFactory(log, postEventsBulk, eventsCache, eventsPushRate, DATA_NAME, latencyTracker); - // Set a timer for the first push of events, + // Set a timer for the first push window of events. + // Not implemented in the base submitter or sync task, since this feature is only used by the events submitter. if (eventsFirstPushWindow > 0) { + let running = false; let stopEventPublisherTimeout: ReturnType; const originalStart = syncTask.start; syncTask.start = () => { + running = true; stopEventPublisherTimeout = setTimeout(originalStart, eventsFirstPushWindow); }; const originalStop = syncTask.stop; syncTask.stop = () => { + running = false; clearTimeout(stopEventPublisherTimeout); originalStop(); }; + syncTask.isRunning = () => { + return running; + }; } // register events submitter to be executed when events cache is full diff --git a/src/utils/settingsValidation/consent.ts b/src/utils/settingsValidation/consent.ts index c68cf222..98b4112b 100644 --- a/src/utils/settingsValidation/consent.ts +++ b/src/utils/settingsValidation/consent.ts @@ -1,11 +1,12 @@ import { ERROR_INVALID_CONFIG_PARAM } from '../../logger/constants'; import { ILogger } from '../../logger/types'; +import { ConsentStatus } from '../../types'; import { CONSENT_DECLINED, CONSENT_GRANTED, CONSENT_UNKNOWN } from '../constants'; import { stringToUpperCase } from '../lang'; const userConsentValues = [CONSENT_DECLINED, CONSENT_GRANTED, CONSENT_UNKNOWN]; -export function validateConsent({ userConsent, log }: { userConsent: any, log: ILogger }) { +export function validateConsent({ userConsent, log }: { userConsent?: any, log: ILogger }): ConsentStatus { userConsent = stringToUpperCase(userConsent); if (userConsentValues.indexOf(userConsent) > -1) return userConsent;