From 0e67f8ae03fe7daa8e883dbdaab021fc8537a2a6 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 23 Feb 2022 13:00:53 -0300 Subject: [PATCH 1/4] implementation and unit tests --- src/logger/constants.ts | 1 + src/logger/messages/warn.ts | 1 + src/sdkClient/__tests__/client.spec.ts | 39 ++++++++++++++++++ src/sdkClient/client.ts | 12 +++--- src/sync/__tests__/syncManagerOnline.spec.ts | 43 ++++++++++++++++++++ src/sync/submitters/eventsSyncTask.ts | 11 +++-- src/sync/submitters/impressionsSyncTask.ts | 11 +++-- src/sync/syncManagerOnline.ts | 22 +++++++--- src/sync/types.ts | 5 ++- src/utils/constants/index.ts | 5 +++ 10 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 src/sdkClient/__tests__/client.spec.ts create mode 100644 src/sync/__tests__/syncManagerOnline.spec.ts diff --git a/src/logger/constants.ts b/src/logger/constants.ts index ada74e13..f9e5041b 100644 --- a/src/logger/constants.ts +++ b/src/logger/constants.ts @@ -93,6 +93,7 @@ export const WARN_SPLITS_FILTER_INVALID = 220; export const WARN_SPLITS_FILTER_EMPTY = 221; export const WARN_API_KEY = 222; export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223; +export const SUBMITTERS_PUSH_FULL_QUEUE_DROPPED = 224; export const ERROR_ENGINE_COMBINER_IFELSEIF = 300; export const ERROR_LOGLEVEL_INVALID = 301; diff --git a/src/logger/messages/warn.ts b/src/logger/messages/warn.ts index f6a66eec..ccda06b8 100644 --- a/src/logger/messages/warn.ts +++ b/src/logger/messages/warn.ts @@ -13,6 +13,7 @@ export const codesWarn: [number, string][] = codesError.concat([ [c.STREAMING_FALLBACK, c.LOG_PREFIX_SYNC_STREAMING + 'Falling back to polling mode. Reason: %s'], [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Droping %s %s after retry. Reason: %s.'], [c.SUBMITTERS_PUSH_RETRY, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Failed to push %s %s, keeping data to retry on next iteration. Reason: %s.'], + [c.SUBMITTERS_PUSH_FULL_QUEUE_DROPPED, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Droping %s from full queue. Reason: user consent declined.'], // client status [c.CLIENT_NOT_READY, '%s: the SDK is not ready, results may be incorrect. Make sure to wait for SDK readiness before using this method.'], [c.CLIENT_NO_LISTENER, 'No listeners for SDK Readiness detected. Incorrect control treatments could have been logged if you called getTreatment/s while the SDK was not yet ready.'], diff --git a/src/sdkClient/__tests__/client.spec.ts b/src/sdkClient/__tests__/client.spec.ts new file mode 100644 index 00000000..f447d6d3 --- /dev/null +++ b/src/sdkClient/__tests__/client.spec.ts @@ -0,0 +1,39 @@ +import { ISettings } from '../../types'; +import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks'; +import { clientFactory } from '../client'; + +test('client should track or not events and impressions depending on user consent status', () => { + const sdkReadinessManager = { readinessManager: { isReady: () => true } }; + const storage = { splits: { trafficTypeExists: () => true } }; + + const settings: ISettings = { ...fullSettings, userConsent: undefined }; + const eventTracker = { track: jest.fn() }; + const impressionsTracker = { track: jest.fn() }; + + // @ts-ignore + const client = clientFactory({ settings, eventTracker, impressionsTracker, sdkReadinessManager, storage }); + + client.getTreatment('some_key', 'some_split'); + expect(impressionsTracker.track).toBeCalledTimes(1); // impression should be tracked if userConsent is undefined + client.track('some_key', 'some_tt', 'some_event'); + expect(eventTracker.track).toBeCalledTimes(1); // event should be tracked if userConsent is undefined + + settings.userConsent = 'unknown'; + client.getTreatment('some_key', 'some_split'); + expect(impressionsTracker.track).toBeCalledTimes(2); // impression should be tracked if userConsent is unknown + client.track('some_key', 'some_tt', 'some_event'); + expect(eventTracker.track).toBeCalledTimes(2); // event should be tracked if userConsent is unknown + + settings.userConsent = 'granted'; + client.getTreatment('some_key', 'some_split'); + expect(impressionsTracker.track).toBeCalledTimes(3); // impression should be tracked if userConsent is granted + client.track('some_key', 'some_tt', 'some_event'); + expect(eventTracker.track).toBeCalledTimes(3); // event should be tracked if userConsent is granted + + settings.userConsent = 'declined'; + client.getTreatment('some_key', 'some_split'); + expect(impressionsTracker.track).toBeCalledTimes(3); // impression should not be tracked if userConsent is declined + client.track('some_key', 'some_tt', 'some_event'); + expect(eventTracker.track).toBeCalledTimes(3); // event should not be tracked if userConsent is declined + +}); diff --git a/src/sdkClient/client.ts b/src/sdkClient/client.ts index 5b0b7de2..7d5ab72c 100644 --- a/src/sdkClient/client.ts +++ b/src/sdkClient/client.ts @@ -4,7 +4,7 @@ import { getMatching, getBucketing } from '../utils/key'; import { validateSplitExistance } from '../utils/inputValidation/splitExistance'; import { validateTrafficTypeExistance } from '../utils/inputValidation/trafficTypeExistance'; import { SDK_NOT_READY } from '../utils/labels'; -import { CONTROL } from '../utils/constants'; +import { CONSENT_DECLINED, CONTROL } from '../utils/constants'; import { IClientFactoryParams } from './types'; import { IEvaluationResult } from '../evaluator/types'; import { SplitIO, ImpressionDTO } from '../types'; @@ -16,13 +16,14 @@ import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants'; */ // @TODO missing time tracking to collect telemetry export function clientFactory(params: IClientFactoryParams): SplitIO.IClient | SplitIO.IAsyncClient { - const { sdkReadinessManager: { readinessManager }, storage, settings: { log, mode }, impressionsTracker, eventTracker } = params; + const { sdkReadinessManager: { readinessManager }, storage, settings, impressionsTracker, eventTracker } = params; + const { log, mode } = settings; function getTreatment(key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, withConfig = false) { const wrapUp = (evaluationResult: IEvaluationResult) => { const queue: ImpressionDTO[] = []; const treatment = processEvaluation(evaluationResult, splitName, key, attributes, withConfig, `getTreatment${withConfig ? 'withConfig' : ''}`, queue); - impressionsTracker.track(queue, attributes); + if (settings.userConsent !== CONSENT_DECLINED) impressionsTracker.track(queue, attributes); return treatment; }; @@ -42,7 +43,7 @@ export function clientFactory(params: IClientFactoryParams): SplitIO.IClient | S Object.keys(evaluationResults).forEach(splitName => { treatments[splitName] = processEvaluation(evaluationResults[splitName], splitName, key, attributes, withConfig, `getTreatments${withConfig ? 'withConfig' : ''}`, queue); }); - impressionsTracker.track(queue, attributes); + if (settings.userConsent !== CONSENT_DECLINED) impressionsTracker.track(queue, attributes); return treatments; }; @@ -115,7 +116,8 @@ export function clientFactory(params: IClientFactoryParams): SplitIO.IClient | S // This may be async but we only warn, we don't actually care if it is valid or not in terms of queueing the event. validateTrafficTypeExistance(log, readinessManager, storage.splits, mode, trafficTypeName, 'track'); - return eventTracker.track(eventData, size); + if (settings.userConsent !== CONSENT_DECLINED) return eventTracker.track(eventData, size); + else return false; } return { diff --git a/src/sync/__tests__/syncManagerOnline.spec.ts b/src/sync/__tests__/syncManagerOnline.spec.ts new file mode 100644 index 00000000..0e8f2a64 --- /dev/null +++ b/src/sync/__tests__/syncManagerOnline.spec.ts @@ -0,0 +1,43 @@ +import { ISettings } from '../../types'; +import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks'; +import { syncTaskFactory } from './syncTask.mock'; + +jest.mock('../submitters/submitterManager', () => { + return { + submitterManagerFactory: syncTaskFactory + }; +}); + +import { syncManagerOnlineFactory } from '../syncManagerOnline'; + +test('syncManagerOnline should start or not the submitter depending on user consent status', () => { + const settings: ISettings = { ...fullSettings, userConsent: undefined }; + + // @ts-ignore + const syncManager = syncManagerOnlineFactory()({ settings }); + const submitter = syncManager.submitter!; + + syncManager.start(); + expect(submitter.start).toBeCalledTimes(1); // Submitter should be started if userConsent is undefined + syncManager.stop(); + expect(submitter.stop).toBeCalledTimes(1); + + settings.userConsent = 'unknown'; + syncManager.start(); + expect(submitter.start).toBeCalledTimes(1); // Submitter should not be started if userConsent is unknown + syncManager.stop(); + expect(submitter.stop).toBeCalledTimes(2); + + settings.userConsent = 'granted'; + syncManager.start(); + expect(submitter.start).toBeCalledTimes(2); // Submitter should be started if userConsent is granted + syncManager.stop(); + expect(submitter.stop).toBeCalledTimes(3); + + settings.userConsent = 'declined'; + syncManager.start(); + expect(submitter.start).toBeCalledTimes(2); // Submitter should not be started if userConsent is declined + syncManager.stop(); + expect(submitter.stop).toBeCalledTimes(4); + +}); diff --git a/src/sync/submitters/eventsSyncTask.ts b/src/sync/submitters/eventsSyncTask.ts index 151607ea..fcd5b3b8 100644 --- a/src/sync/submitters/eventsSyncTask.ts +++ b/src/sync/submitters/eventsSyncTask.ts @@ -3,7 +3,7 @@ import { IPostEventsBulk } from '../../services/types'; import { ISyncTask, ITimeTracker } from '../types'; import { submitterSyncTaskFactory } from './submitterSyncTask'; import { ILogger } from '../../logger/types'; -import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; +import { SUBMITTERS_PUSH_FULL_QUEUE, SUBMITTERS_PUSH_FULL_QUEUE_DROPPED } from '../../logger/constants'; const DATA_NAME = 'events'; @@ -38,8 +38,13 @@ export function eventsSyncTaskFactory( // register events submitter to be executed when events cache is full eventsCache.setOnFullQueueCb(() => { - log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); - syncTask.execute(); + if (syncTask.isRunning()) { + log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); + syncTask.execute(); + } else { // If submitter is stopped (e.g., user consent declined, or app state offline), we drop items + log.warn(SUBMITTERS_PUSH_FULL_QUEUE_DROPPED); + eventsCache.clear(); + } }); return syncTask; diff --git a/src/sync/submitters/impressionsSyncTask.ts b/src/sync/submitters/impressionsSyncTask.ts index 5947c40c..9f225fdd 100644 --- a/src/sync/submitters/impressionsSyncTask.ts +++ b/src/sync/submitters/impressionsSyncTask.ts @@ -6,7 +6,7 @@ import { ImpressionDTO } from '../../types'; import { submitterSyncTaskFactory } from './submitterSyncTask'; import { ImpressionsPayload } from './types'; import { ILogger } from '../../logger/types'; -import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; +import { SUBMITTERS_PUSH_FULL_QUEUE, SUBMITTERS_PUSH_FULL_QUEUE_DROPPED } from '../../logger/constants'; const DATA_NAME = 'impressions'; @@ -57,8 +57,13 @@ export function impressionsSyncTaskFactory( // register impressions submitter to be executed when impressions cache is full impressionsCache.setOnFullQueueCb(() => { - log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); - syncTask.execute(); + if (syncTask.isRunning()) { + log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); + syncTask.execute(); + } else { // If submitter is stopped (e.g., user consent declined, or app state offline), we drop items + log.warn(SUBMITTERS_PUSH_FULL_QUEUE_DROPPED); + impressionsCache.clear(); + } }); return syncTask; diff --git a/src/sync/syncManagerOnline.ts b/src/sync/syncManagerOnline.ts index 1bbfe948..425b9d9f 100644 --- a/src/sync/syncManagerOnline.ts +++ b/src/sync/syncManagerOnline.ts @@ -6,6 +6,7 @@ import { IPushManager } from './streaming/types'; import { IPollingManager, IPollingManagerCS } from './polling/types'; import { PUSH_SUBSYSTEM_UP, PUSH_SUBSYSTEM_DOWN } from './streaming/constants'; import { SYNC_START_POLLING, SYNC_CONTINUE_POLLING, SYNC_STOP_POLLING } from '../logger/constants'; +import { CONSENT_GRANTED } from '../utils/constants'; /** * Online SyncManager factory. @@ -39,6 +40,11 @@ export function syncManagerOnlineFactory( // It is not inyected as push and polling managers, because at the moment it is required const submitter = submitterManagerFactory(params); + function isUserConsentGranted() { + const userConsent = params.settings.userConsent; + // undefined userConsent is handled as granted to avoid a breaking change with users that don't use this features + return !userConsent || userConsent === CONSENT_GRANTED; + } /** Sync Manager logic */ @@ -69,12 +75,18 @@ export function syncManagerOnlineFactory( let startFirstTime = true; // flag to distinguish calling the `start` method for the first time, to support pausing and resuming the synchronization return { + // Exposed for fine-grained control of synchronization. + // E.g.: user consent, app state changes (Page hide, Foreground/Background, Online/Offline). + pollingManager, pushManager, + submitter, /** * Method used to start the syncManager for the first time, or resume it after being stopped. */ start() { + running = true; + // start syncing splits and segments if (pollingManager) { if (pushManager) { @@ -90,21 +102,21 @@ export function syncManagerOnlineFactory( } // start periodic data recording (events, impressions, telemetry). - if (submitter) submitter.start(); - running = true; + if (isUserConsentGranted()) submitter.start(); }, /** * Method used to stop/pause the syncManager. */ stop() { + running = false; + // stop syncing if (pushManager) pushManager.stop(); if (pollingManager && pollingManager.isRunning()) pollingManager.stop(); // stop periodic data recording (events, impressions, telemetry). - if (submitter) submitter.stop(); - running = false; + submitter.stop(); }, isRunning() { @@ -112,7 +124,7 @@ export function syncManagerOnlineFactory( }, flush() { - if (submitter) return submitter.execute(); + if (isUserConsentGranted()) return submitter.execute(); else return Promise.resolve(); }, diff --git a/src/sync/types.ts b/src/sync/types.ts index 0f0588d9..1a70a5c6 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -3,6 +3,7 @@ import { IPlatform } from '../sdkFactory/types'; import { ISplitApi } from '../services/types'; import { IStorageSync } from '../storages/types'; import { ISettings } from '../types'; +import { IPollingManager } from './polling/types'; import { IPushManager } from './streaming/types'; export interface ITask { @@ -43,7 +44,9 @@ export interface ITimeTracker { export interface ISyncManager extends ITask { flush(): Promise, - pushManager?: IPushManager + pushManager?: IPushManager, + pollingManager?: IPollingManager, + submitter?: ISyncTask } export interface ISyncManagerCS extends ISyncManager { diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts index 4a4d8303..9b3412a5 100644 --- a/src/utils/constants/index.ts +++ b/src/utils/constants/index.ts @@ -32,3 +32,8 @@ export const STORAGE_MEMORY: StorageType = 'MEMORY'; export const STORAGE_LOCALSTORAGE: StorageType = 'LOCALSTORAGE'; export const STORAGE_REDIS: StorageType = 'REDIS'; export const STORAGE_PLUGGABLE: StorageType = 'PLUGGABLE'; + +// User consent +export const CONSENT_GRANTED = 'granted'; // The user has granted consent for tracking events and impressions +export const CONSENT_DECLINED = 'declined'; // The user has declined consent for tracking events and impressions +export const CONSENT_UNKNOWN = 'unknown'; // The user has neither granted nor declined consent for tracking events and impressions From 58620798732acbb4c2828612ec32819dfe472a3d Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 23 Feb 2022 13:29:14 -0300 Subject: [PATCH 2/4] updated settings interface --- src/types.ts | 6 ++++++ src/utils/settingsValidation/__tests__/settings.mocks.ts | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index 1e3654a8..9f67b027 100644 --- a/src/types.ts +++ b/src/types.ts @@ -54,6 +54,11 @@ type EventConsts = { * @typedef {string} SDKMode */ export type SDKMode = 'standalone' | 'consumer' | 'localhost' | 'consumer_partial'; +/** + * User consent status. + * @typedef {string} ConsentStatus + */ +export type ConsentStatus = 'granted' | 'declined' | 'unknown'; /** * Settings interface. This is a representation of the settings the SDK expose, that's why * most of it's props are readonly. Only features should be rewritten when localhost mode is active. @@ -111,6 +116,7 @@ export interface ISettings { }, readonly log: ILogger readonly impressionListener?: unknown + userConsent?: ConsentStatus } /** * Log levels. diff --git a/src/utils/settingsValidation/__tests__/settings.mocks.ts b/src/utils/settingsValidation/__tests__/settings.mocks.ts index 3cb458cb..ff160297 100644 --- a/src/utils/settingsValidation/__tests__/settings.mocks.ts +++ b/src/utils/settingsValidation/__tests__/settings.mocks.ts @@ -88,7 +88,8 @@ export const fullSettings: ISettings = { auth: 'auth', streaming: 'streaming' }, - log: loggerMock + log: loggerMock, + userConsent: undefined }; export const fullSettingsServerSide = { From b2dfd2e494993eea48f0da4fdaf6c11cd2394579 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 23 Feb 2022 14:11:29 -0300 Subject: [PATCH 3/4] update submitters to not drop data if queues are full but consent has not been granted yet --- src/logger/constants.ts | 1 - src/logger/messages/warn.ts | 1 - src/sync/submitters/eventsSyncTask.ts | 7 +++---- src/sync/submitters/impressionsSyncTask.ts | 7 +++---- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/logger/constants.ts b/src/logger/constants.ts index f9e5041b..ada74e13 100644 --- a/src/logger/constants.ts +++ b/src/logger/constants.ts @@ -93,7 +93,6 @@ export const WARN_SPLITS_FILTER_INVALID = 220; export const WARN_SPLITS_FILTER_EMPTY = 221; export const WARN_API_KEY = 222; export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223; -export const SUBMITTERS_PUSH_FULL_QUEUE_DROPPED = 224; export const ERROR_ENGINE_COMBINER_IFELSEIF = 300; export const ERROR_LOGLEVEL_INVALID = 301; diff --git a/src/logger/messages/warn.ts b/src/logger/messages/warn.ts index ccda06b8..f6a66eec 100644 --- a/src/logger/messages/warn.ts +++ b/src/logger/messages/warn.ts @@ -13,7 +13,6 @@ export const codesWarn: [number, string][] = codesError.concat([ [c.STREAMING_FALLBACK, c.LOG_PREFIX_SYNC_STREAMING + 'Falling back to polling mode. Reason: %s'], [c.SUBMITTERS_PUSH_FAILS, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Droping %s %s after retry. Reason: %s.'], [c.SUBMITTERS_PUSH_RETRY, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Failed to push %s %s, keeping data to retry on next iteration. Reason: %s.'], - [c.SUBMITTERS_PUSH_FULL_QUEUE_DROPPED, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Droping %s from full queue. Reason: user consent declined.'], // client status [c.CLIENT_NOT_READY, '%s: the SDK is not ready, results may be incorrect. Make sure to wait for SDK readiness before using this method.'], [c.CLIENT_NO_LISTENER, 'No listeners for SDK Readiness detected. Incorrect control treatments could have been logged if you called getTreatment/s while the SDK was not yet ready.'], diff --git a/src/sync/submitters/eventsSyncTask.ts b/src/sync/submitters/eventsSyncTask.ts index fcd5b3b8..b87ee24e 100644 --- a/src/sync/submitters/eventsSyncTask.ts +++ b/src/sync/submitters/eventsSyncTask.ts @@ -3,7 +3,7 @@ import { IPostEventsBulk } from '../../services/types'; import { ISyncTask, ITimeTracker } from '../types'; import { submitterSyncTaskFactory } from './submitterSyncTask'; import { ILogger } from '../../logger/types'; -import { SUBMITTERS_PUSH_FULL_QUEUE, SUBMITTERS_PUSH_FULL_QUEUE_DROPPED } from '../../logger/constants'; +import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; const DATA_NAME = 'events'; @@ -41,10 +41,9 @@ export function eventsSyncTaskFactory( if (syncTask.isRunning()) { log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); syncTask.execute(); - } else { // If submitter is stopped (e.g., user consent declined, or app state offline), we drop items - log.warn(SUBMITTERS_PUSH_FULL_QUEUE_DROPPED); - eventsCache.clear(); } + // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data. + // Data will be sent when submitter is resumed. }); return syncTask; diff --git a/src/sync/submitters/impressionsSyncTask.ts b/src/sync/submitters/impressionsSyncTask.ts index 9f225fdd..f3fdb20c 100644 --- a/src/sync/submitters/impressionsSyncTask.ts +++ b/src/sync/submitters/impressionsSyncTask.ts @@ -6,7 +6,7 @@ import { ImpressionDTO } from '../../types'; import { submitterSyncTaskFactory } from './submitterSyncTask'; import { ImpressionsPayload } from './types'; import { ILogger } from '../../logger/types'; -import { SUBMITTERS_PUSH_FULL_QUEUE, SUBMITTERS_PUSH_FULL_QUEUE_DROPPED } from '../../logger/constants'; +import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants'; const DATA_NAME = 'impressions'; @@ -60,10 +60,9 @@ export function impressionsSyncTaskFactory( if (syncTask.isRunning()) { log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]); syncTask.execute(); - } else { // If submitter is stopped (e.g., user consent declined, or app state offline), we drop items - log.warn(SUBMITTERS_PUSH_FULL_QUEUE_DROPPED); - impressionsCache.clear(); } + // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data. + // Data will be sent when submitter is resumed. }); return syncTask; From 961f56c5bdcf5e7a82bf18a80852e5728858666a Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 24 Feb 2022 16:35:12 -0300 Subject: [PATCH 4/4] constants to uppercase --- src/sdkClient/__tests__/client.spec.ts | 9 ++++----- src/sync/__tests__/syncManagerOnline.spec.ts | 9 ++++----- src/types.ts | 4 ++-- src/utils/constants/index.ts | 6 +++--- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/sdkClient/__tests__/client.spec.ts b/src/sdkClient/__tests__/client.spec.ts index f447d6d3..4112d7d5 100644 --- a/src/sdkClient/__tests__/client.spec.ts +++ b/src/sdkClient/__tests__/client.spec.ts @@ -1,4 +1,3 @@ -import { ISettings } from '../../types'; import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks'; import { clientFactory } from '../client'; @@ -6,7 +5,7 @@ test('client should track or not events and impressions depending on user consen const sdkReadinessManager = { readinessManager: { isReady: () => true } }; const storage = { splits: { trafficTypeExists: () => true } }; - const settings: ISettings = { ...fullSettings, userConsent: undefined }; + const settings = { ...fullSettings }; const eventTracker = { track: jest.fn() }; const impressionsTracker = { track: jest.fn() }; @@ -18,19 +17,19 @@ test('client should track or not events and impressions depending on user consen client.track('some_key', 'some_tt', 'some_event'); expect(eventTracker.track).toBeCalledTimes(1); // event should be tracked if userConsent is undefined - settings.userConsent = 'unknown'; + settings.userConsent = 'UNKNOWN'; client.getTreatment('some_key', 'some_split'); expect(impressionsTracker.track).toBeCalledTimes(2); // impression should be tracked if userConsent is unknown client.track('some_key', 'some_tt', 'some_event'); expect(eventTracker.track).toBeCalledTimes(2); // event should be tracked if userConsent is unknown - settings.userConsent = 'granted'; + settings.userConsent = 'GRANTED'; client.getTreatment('some_key', 'some_split'); expect(impressionsTracker.track).toBeCalledTimes(3); // impression should be tracked if userConsent is granted client.track('some_key', 'some_tt', 'some_event'); expect(eventTracker.track).toBeCalledTimes(3); // event should be tracked if userConsent is granted - settings.userConsent = 'declined'; + settings.userConsent = 'DECLINED'; client.getTreatment('some_key', 'some_split'); expect(impressionsTracker.track).toBeCalledTimes(3); // impression should not be tracked if userConsent is declined client.track('some_key', 'some_tt', 'some_event'); diff --git a/src/sync/__tests__/syncManagerOnline.spec.ts b/src/sync/__tests__/syncManagerOnline.spec.ts index 0e8f2a64..c47cad86 100644 --- a/src/sync/__tests__/syncManagerOnline.spec.ts +++ b/src/sync/__tests__/syncManagerOnline.spec.ts @@ -1,4 +1,3 @@ -import { ISettings } from '../../types'; import { fullSettings } from '../../utils/settingsValidation/__tests__/settings.mocks'; import { syncTaskFactory } from './syncTask.mock'; @@ -11,7 +10,7 @@ jest.mock('../submitters/submitterManager', () => { import { syncManagerOnlineFactory } from '../syncManagerOnline'; test('syncManagerOnline should start or not the submitter depending on user consent status', () => { - const settings: ISettings = { ...fullSettings, userConsent: undefined }; + const settings = { ...fullSettings }; // @ts-ignore const syncManager = syncManagerOnlineFactory()({ settings }); @@ -22,19 +21,19 @@ test('syncManagerOnline should start or not the submitter depending on user cons syncManager.stop(); expect(submitter.stop).toBeCalledTimes(1); - settings.userConsent = 'unknown'; + settings.userConsent = 'UNKNOWN'; syncManager.start(); expect(submitter.start).toBeCalledTimes(1); // Submitter should not be started if userConsent is unknown syncManager.stop(); expect(submitter.stop).toBeCalledTimes(2); - settings.userConsent = 'granted'; + settings.userConsent = 'GRANTED'; syncManager.start(); expect(submitter.start).toBeCalledTimes(2); // Submitter should be started if userConsent is granted syncManager.stop(); expect(submitter.stop).toBeCalledTimes(3); - settings.userConsent = 'declined'; + settings.userConsent = 'DECLINED'; syncManager.start(); expect(submitter.start).toBeCalledTimes(2); // Submitter should not be started if userConsent is declined syncManager.stop(); diff --git a/src/types.ts b/src/types.ts index 9f67b027..c449ff37 100644 --- a/src/types.ts +++ b/src/types.ts @@ -58,7 +58,7 @@ export type SDKMode = 'standalone' | 'consumer' | 'localhost' | 'consumer_partia * User consent status. * @typedef {string} ConsentStatus */ -export type ConsentStatus = 'granted' | 'declined' | 'unknown'; +export type ConsentStatus = 'GRANTED' | 'DECLINED' | 'UNKNOWN'; /** * Settings interface. This is a representation of the settings the SDK expose, that's why * most of it's props are readonly. Only features should be rewritten when localhost mode is active. @@ -116,7 +116,7 @@ export interface ISettings { }, readonly log: ILogger readonly impressionListener?: unknown - userConsent?: ConsentStatus + readonly userConsent?: ConsentStatus } /** * Log levels. diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts index 9b3412a5..243accd6 100644 --- a/src/utils/constants/index.ts +++ b/src/utils/constants/index.ts @@ -34,6 +34,6 @@ export const STORAGE_REDIS: StorageType = 'REDIS'; export const STORAGE_PLUGGABLE: StorageType = 'PLUGGABLE'; // User consent -export const CONSENT_GRANTED = 'granted'; // The user has granted consent for tracking events and impressions -export const CONSENT_DECLINED = 'declined'; // The user has declined consent for tracking events and impressions -export const CONSENT_UNKNOWN = 'unknown'; // The user has neither granted nor declined consent for tracking events and impressions +export const CONSENT_GRANTED = 'GRANTED'; // The user has granted consent for tracking events and impressions +export const CONSENT_DECLINED = 'DECLINED'; // The user has declined consent for tracking events and impressions +export const CONSENT_UNKNOWN = 'UNKNOWN'; // The user has neither granted nor declined consent for tracking events and impressions