@splitsoftware/splitio-commons 2.0.2 → 2.0.3-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGES.txt +4 -0
  2. package/cjs/evaluator/index.js +2 -0
  3. package/cjs/listeners/browser.js +4 -6
  4. package/cjs/sdkClient/client.js +13 -13
  5. package/cjs/sdkClient/sdkClient.js +1 -1
  6. package/cjs/sdkFactory/index.js +9 -14
  7. package/cjs/sdkManager/index.js +2 -1
  8. package/cjs/storages/inLocalStorage/index.js +4 -3
  9. package/cjs/storages/inMemory/InMemoryStorage.js +3 -3
  10. package/cjs/storages/inMemory/InMemoryStorageCS.js +4 -3
  11. package/cjs/storages/inRedis/index.js +9 -13
  12. package/cjs/storages/pluggable/index.js +15 -19
  13. package/cjs/sync/submitters/impressionCountsSubmitter.js +2 -4
  14. package/cjs/sync/submitters/submitterManager.js +3 -6
  15. package/cjs/trackers/impressionsTracker.js +17 -18
  16. package/cjs/trackers/strategy/strategyDebug.js +4 -11
  17. package/cjs/trackers/strategy/strategyNone.js +11 -16
  18. package/cjs/trackers/strategy/strategyOptimized.js +11 -21
  19. package/esm/evaluator/index.js +2 -0
  20. package/esm/listeners/browser.js +1 -3
  21. package/esm/sdkClient/client.js +13 -13
  22. package/esm/sdkClient/sdkClient.js +1 -1
  23. package/esm/sdkFactory/index.js +10 -15
  24. package/esm/sdkManager/index.js +2 -1
  25. package/esm/storages/inLocalStorage/index.js +5 -4
  26. package/esm/storages/inMemory/InMemoryStorage.js +4 -4
  27. package/esm/storages/inMemory/InMemoryStorageCS.js +5 -4
  28. package/esm/storages/inRedis/index.js +10 -14
  29. package/esm/storages/pluggable/index.js +16 -20
  30. package/esm/sync/submitters/impressionCountsSubmitter.js +2 -4
  31. package/esm/sync/submitters/submitterManager.js +3 -6
  32. package/esm/trackers/impressionsTracker.js +17 -18
  33. package/esm/trackers/strategy/strategyDebug.js +4 -11
  34. package/esm/trackers/strategy/strategyNone.js +11 -16
  35. package/esm/trackers/strategy/strategyOptimized.js +11 -21
  36. package/package.json +1 -1
  37. package/src/dtos/types.ts +2 -1
  38. package/src/evaluator/index.ts +2 -0
  39. package/src/evaluator/types.ts +1 -1
  40. package/src/listeners/browser.ts +1 -3
  41. package/src/sdkClient/client.ts +11 -11
  42. package/src/sdkClient/sdkClient.ts +1 -1
  43. package/src/sdkFactory/index.ts +11 -16
  44. package/src/sdkFactory/types.ts +1 -1
  45. package/src/sdkManager/index.ts +2 -1
  46. package/src/storages/inLocalStorage/index.ts +5 -4
  47. package/src/storages/inMemory/InMemoryStorage.ts +4 -4
  48. package/src/storages/inMemory/InMemoryStorageCS.ts +5 -4
  49. package/src/storages/inRedis/index.ts +10 -10
  50. package/src/storages/pluggable/index.ts +16 -20
  51. package/src/storages/types.ts +2 -2
  52. package/src/sync/submitters/impressionCountsSubmitter.ts +2 -4
  53. package/src/sync/submitters/submitterManager.ts +3 -4
  54. package/src/sync/submitters/uniqueKeysSubmitter.ts +2 -3
  55. package/src/trackers/impressionsTracker.ts +17 -18
  56. package/src/trackers/strategy/strategyDebug.ts +4 -11
  57. package/src/trackers/strategy/strategyNone.ts +11 -17
  58. package/src/trackers/strategy/strategyOptimized.ts +10 -20
  59. package/src/trackers/types.ts +2 -8
  60. package/types/splitio.d.ts +4 -0
@@ -25,7 +25,7 @@ export interface IEvaluation {
25
25
  config?: string | null
26
26
  }
27
27
 
28
- export type IEvaluationResult = IEvaluation & { treatment: string }
28
+ export type IEvaluationResult = IEvaluation & { treatment: string; track?: boolean }
29
29
 
30
30
  export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
31
31
 
@@ -8,7 +8,6 @@ import { IResponse, ISplitApi } from '../services/types';
8
8
  import { ISettings } from '../types';
9
9
  import SplitIO from '../../types/splitio';
10
10
  import { ImpressionsPayload } from '../sync/submitters/types';
11
- import { OPTIMIZED, DEBUG, NONE } from '../utils/constants';
12
11
  import { objectAssign } from '../utils/lang/objectAssign';
13
12
  import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
14
13
  import { ISyncManager } from '../sync/types';
@@ -78,10 +77,9 @@ export class BrowserSignalListener implements ISignalListener {
78
77
 
79
78
  // Flush impressions & events data if there is user consent
80
79
  if (isConsentGranted(this.settings)) {
81
- const sim = this.settings.sync.impressionsMode;
82
80
  const extraMetadata = {
83
81
  // sim stands for Sync/Split Impressions Mode
84
- sim: sim === OPTIMIZED ? OPTIMIZED : sim === DEBUG ? DEBUG : NONE
82
+ sim: this.settings.sync.impressionsMode
85
83
  };
86
84
 
87
85
  this._flushData(events + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);
@@ -34,11 +34,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
34
34
  const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
35
35
 
36
36
  const wrapUp = (evaluationResult: IEvaluationResult) => {
37
- const queue: SplitIO.ImpressionDTO[] = [];
37
+ const queue: [impression: SplitIO.ImpressionDTO, track?: boolean][] = [];
38
38
  const treatment = processEvaluation(evaluationResult, featureFlagName, key, attributes, withConfig, methodName, queue);
39
39
  impressionsTracker.track(queue, attributes);
40
40
 
41
- stopTelemetryTracker(queue[0] && queue[0].label);
41
+ stopTelemetryTracker(queue[0] && queue[0][0].label);
42
42
  return treatment;
43
43
  };
44
44
 
@@ -59,14 +59,14 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
59
59
  const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS);
60
60
 
61
61
  const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
62
- const queue: SplitIO.ImpressionDTO[] = [];
62
+ const queue: [impression: SplitIO.ImpressionDTO, track?: boolean][] = [];
63
63
  const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
64
64
  Object.keys(evaluationResults).forEach(featureFlagName => {
65
65
  treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, attributes, withConfig, methodName, queue);
66
66
  });
67
67
  impressionsTracker.track(queue, attributes);
68
68
 
69
- stopTelemetryTracker(queue[0] && queue[0].label);
69
+ stopTelemetryTracker(queue[0] && queue[0][0].label);
70
70
  return treatments;
71
71
  };
72
72
 
@@ -87,7 +87,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
87
87
  const stopTelemetryTracker = telemetryTracker.trackEval(method);
88
88
 
89
89
  const wrapUp = (evaluationResults: Record<string, IEvaluationResult>) => {
90
- const queue: SplitIO.ImpressionDTO[] = [];
90
+ const queue: [impression: SplitIO.ImpressionDTO, track?: boolean][] = [];
91
91
  const treatments: Record<string, SplitIO.Treatment | SplitIO.TreatmentWithConfig> = {};
92
92
  const evaluations = evaluationResults;
93
93
  Object.keys(evaluations).forEach(featureFlagName => {
@@ -95,7 +95,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
95
95
  });
96
96
  impressionsTracker.track(queue, attributes);
97
97
 
98
- stopTelemetryTracker(queue[0] && queue[0].label);
98
+ stopTelemetryTracker(queue[0] && queue[0][0].label);
99
99
  return treatments;
100
100
  };
101
101
 
@@ -128,25 +128,25 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
128
128
  attributes: SplitIO.Attributes | undefined,
129
129
  withConfig: boolean,
130
130
  invokingMethodName: string,
131
- queue: SplitIO.ImpressionDTO[]
131
+ queue: [impression: SplitIO.ImpressionDTO, track?: boolean][]
132
132
  ): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
133
133
  const matchingKey = getMatching(key);
134
134
  const bucketingKey = getBucketing(key);
135
135
 
136
- const { treatment, label, changeNumber, config = null } = evaluation;
136
+ const { treatment, label, changeNumber, config = null, track } = evaluation;
137
137
  log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]);
138
138
 
139
139
  if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
140
140
  log.info(IMPRESSION_QUEUEING);
141
- queue.push({
141
+ queue.push([{
142
142
  feature: featureFlagName,
143
143
  keyName: matchingKey,
144
144
  treatment,
145
145
  time: Date.now(),
146
146
  bucketingKey,
147
147
  label,
148
- changeNumber: changeNumber as number
149
- });
148
+ changeNumber: changeNumber as number,
149
+ }, track]);
150
150
  }
151
151
 
152
152
  if (withConfig) {
@@ -61,7 +61,7 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
61
61
  releaseApiKey(settings.core.authorizationKey);
62
62
  telemetryTracker.sessionLength();
63
63
  signalListener && signalListener.stop();
64
- uniqueKeysTracker && uniqueKeysTracker.stop();
64
+ uniqueKeysTracker.stop();
65
65
  }
66
66
 
67
67
  // Stop background jobs
@@ -13,7 +13,7 @@ import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
13
13
  import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
14
14
  import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
15
15
  import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
16
- import { NONE, OPTIMIZED } from '../utils/constants';
16
+ import { DEBUG, OPTIMIZED } from '../utils/constants';
17
17
 
18
18
  /**
19
19
  * Modular SDK factory
@@ -59,21 +59,16 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
59
59
  const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
60
60
 
61
61
  const observer = impressionsObserverFactory();
62
- const uniqueKeysTracker = impressionsMode === NONE ? uniqueKeysTrackerFactory(log, storage.uniqueKeys!, filterAdapterFactory && filterAdapterFactory()) : undefined;
63
-
64
- let strategy;
65
- switch (impressionsMode) {
66
- case OPTIMIZED:
67
- strategy = strategyOptimizedFactory(observer, storage.impressionCounts!);
68
- break;
69
- case NONE:
70
- strategy = strategyNoneFactory(storage.impressionCounts!, uniqueKeysTracker!);
71
- break;
72
- default:
73
- strategy = strategyDebugFactory(observer);
74
- }
62
+ const uniqueKeysTracker = uniqueKeysTrackerFactory(log, storage.uniqueKeys, filterAdapterFactory && filterAdapterFactory());
63
+
64
+ const noneStrategy = strategyNoneFactory(storage.impressionCounts, uniqueKeysTracker);
65
+ const strategy = impressionsMode === OPTIMIZED ?
66
+ strategyOptimizedFactory(observer, storage.impressionCounts) :
67
+ impressionsMode === DEBUG ?
68
+ strategyDebugFactory(observer) :
69
+ noneStrategy;
75
70
 
76
- const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, whenInit, integrationsManager, storage.telemetry);
71
+ const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, noneStrategy, strategy, whenInit, integrationsManager, storage.telemetry);
77
72
  const eventTracker = eventTrackerFactory(settings, storage.events, whenInit, integrationsManager, storage.telemetry);
78
73
 
79
74
  // splitApi is used by SyncManager and Browser signal listener
@@ -99,7 +94,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
99
94
  // We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
100
95
  validateAndTrackApiKey(log, settings.core.authorizationKey);
101
96
  readiness.init();
102
- uniqueKeysTracker && uniqueKeysTracker.start();
97
+ uniqueKeysTracker.start();
103
98
  syncManager && syncManager.start();
104
99
  signalListener && signalListener.start();
105
100
 
@@ -46,7 +46,7 @@ export interface ISdkFactoryContext {
46
46
  eventTracker: IEventTracker,
47
47
  telemetryTracker: ITelemetryTracker,
48
48
  storage: IStorageSync | IStorageAsync,
49
- uniqueKeysTracker?: IUniqueKeysTracker,
49
+ uniqueKeysTracker: IUniqueKeysTracker,
50
50
  signalListener?: ISignalListener
51
51
  splitApi?: ISplitApi
52
52
  syncManager?: ISyncManager,
@@ -31,7 +31,8 @@ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
31
31
  treatments: collectTreatments(splitObject),
32
32
  configs: splitObject.configurations || {},
33
33
  sets: splitObject.sets || [],
34
- defaultTreatment: splitObject.defaultTreatment
34
+ defaultTreatment: splitObject.defaultTreatment,
35
+ trackImpressions: splitObject.trackImpressions !== false
35
36
  };
36
37
  }
37
38
 
@@ -10,7 +10,7 @@ import { MySegmentsCacheInLocal } from './MySegmentsCacheInLocal';
10
10
  import { DEFAULT_CACHE_EXPIRATION_IN_MILLIS } from '../../utils/constants/browser';
11
11
  import { InMemoryStorageCSFactory } from '../inMemory/InMemoryStorageCS';
12
12
  import { LOG_PREFIX } from './constants';
13
- import { DEBUG, NONE, STORAGE_LOCALSTORAGE } from '../../utils/constants';
13
+ import { STORAGE_LOCALSTORAGE } from '../../utils/constants';
14
14
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
15
15
  import { UniqueKeysCacheInMemoryCS } from '../inMemory/UniqueKeysCacheInMemoryCS';
16
16
  import { getMatching } from '../../utils/key';
@@ -34,7 +34,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
34
34
  return InMemoryStorageCSFactory(params);
35
35
  }
36
36
 
37
- const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode } } } = params;
37
+ const { settings, settings: { log, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
38
38
  const matchingKey = getMatching(settings.core.key);
39
39
  const keys = new KeyBuilderCS(prefix, matchingKey);
40
40
  const expirationTimestamp = Date.now() - DEFAULT_CACHE_EXPIRATION_IN_MILLIS;
@@ -48,10 +48,10 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
48
48
  segments,
49
49
  largeSegments,
50
50
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
51
- impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
51
+ impressionCounts: new ImpressionCountsCacheInMemory(),
52
52
  events: new EventsCacheInMemory(eventsQueueSize),
53
53
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
54
- uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
54
+ uniqueKeys: new UniqueKeysCacheInMemoryCS(),
55
55
 
56
56
  destroy() { },
57
57
 
@@ -66,6 +66,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
66
66
  impressionCounts: this.impressionCounts,
67
67
  events: this.events,
68
68
  telemetry: this.telemetry,
69
+ uniqueKeys: this.uniqueKeys,
69
70
 
70
71
  destroy() { }
71
72
  };
@@ -4,7 +4,7 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
4
4
  import { EventsCacheInMemory } from './EventsCacheInMemory';
5
5
  import { IStorageFactoryParams, IStorageSync } from '../types';
6
6
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
- import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
7
+ import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
9
  import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
10
10
 
@@ -14,7 +14,7 @@ import { UniqueKeysCacheInMemory } from './UniqueKeysCacheInMemory';
14
14
  * @param params - parameters required by EventsCacheSync
15
15
  */
16
16
  export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageSync {
17
- const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
17
+ const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { __splitFiltersValidation } } } = params;
18
18
 
19
19
  const splits = new SplitsCacheInMemory(__splitFiltersValidation);
20
20
  const segments = new SegmentsCacheInMemory();
@@ -23,10 +23,10 @@ export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageS
23
23
  splits,
24
24
  segments,
25
25
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
26
- impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
26
+ impressionCounts: new ImpressionCountsCacheInMemory(),
27
27
  events: new EventsCacheInMemory(eventsQueueSize),
28
28
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
29
- uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemory() : undefined,
29
+ uniqueKeys: new UniqueKeysCacheInMemory(),
30
30
 
31
31
  destroy() { }
32
32
  };
@@ -4,7 +4,7 @@ import { ImpressionsCacheInMemory } from './ImpressionsCacheInMemory';
4
4
  import { EventsCacheInMemory } from './EventsCacheInMemory';
5
5
  import { IStorageSync, IStorageFactoryParams } from '../types';
6
6
  import { ImpressionCountsCacheInMemory } from './ImpressionCountsCacheInMemory';
7
- import { DEBUG, LOCALHOST_MODE, NONE, STORAGE_MEMORY } from '../../utils/constants';
7
+ import { LOCALHOST_MODE, STORAGE_MEMORY } from '../../utils/constants';
8
8
  import { shouldRecordTelemetry, TelemetryCacheInMemory } from './TelemetryCacheInMemory';
9
9
  import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
10
10
 
@@ -14,7 +14,7 @@ import { UniqueKeysCacheInMemoryCS } from './UniqueKeysCacheInMemoryCS';
14
14
  * @param params - parameters required by EventsCacheSync
15
15
  */
16
16
  export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync {
17
- const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize, }, sync: { impressionsMode, __splitFiltersValidation } } } = params;
17
+ const { settings: { scheduler: { impressionsQueueSize, eventsQueueSize }, sync: { __splitFiltersValidation } } } = params;
18
18
 
19
19
  const splits = new SplitsCacheInMemory(__splitFiltersValidation);
20
20
  const segments = new MySegmentsCacheInMemory();
@@ -25,10 +25,10 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
25
25
  segments,
26
26
  largeSegments,
27
27
  impressions: new ImpressionsCacheInMemory(impressionsQueueSize),
28
- impressionCounts: impressionsMode !== DEBUG ? new ImpressionCountsCacheInMemory() : undefined,
28
+ impressionCounts: new ImpressionCountsCacheInMemory(),
29
29
  events: new EventsCacheInMemory(eventsQueueSize),
30
30
  telemetry: shouldRecordTelemetry(params) ? new TelemetryCacheInMemory(splits, segments) : undefined,
31
- uniqueKeys: impressionsMode === NONE ? new UniqueKeysCacheInMemoryCS() : undefined,
31
+ uniqueKeys: new UniqueKeysCacheInMemoryCS(),
32
32
 
33
33
  destroy() { },
34
34
 
@@ -42,6 +42,7 @@ export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorag
42
42
  impressionCounts: this.impressionCounts,
43
43
  events: this.events,
44
44
  telemetry: this.telemetry,
45
+ uniqueKeys: this.uniqueKeys,
45
46
 
46
47
  destroy() { }
47
48
  };
@@ -6,7 +6,7 @@ import { SplitsCacheInRedis } from './SplitsCacheInRedis';
6
6
  import { SegmentsCacheInRedis } from './SegmentsCacheInRedis';
7
7
  import { ImpressionsCacheInRedis } from './ImpressionsCacheInRedis';
8
8
  import { EventsCacheInRedis } from './EventsCacheInRedis';
9
- import { DEBUG, NONE, STORAGE_REDIS } from '../../utils/constants';
9
+ import { STORAGE_REDIS } from '../../utils/constants';
10
10
  import { TelemetryCacheInRedis } from './TelemetryCacheInRedis';
11
11
  import { UniqueKeysCacheInRedis } from './UniqueKeysCacheInRedis';
12
12
  import { ImpressionCountsCacheInRedis } from './ImpressionCountsCacheInRedis';
@@ -30,19 +30,19 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
30
30
  const prefix = validatePrefix(options.prefix);
31
31
 
32
32
  function InRedisStorageFactory(params: IStorageFactoryParams): IStorageAsync {
33
- const { onReadyCb, settings, settings: { log, sync: { impressionsMode } } } = params;
33
+ const { onReadyCb, settings, settings: { log } } = params;
34
34
  const metadata = metadataBuilder(settings);
35
35
  const keys = new KeyBuilderSS(prefix, metadata);
36
36
  const redisClient: RedisAdapter = new RD(log, options.options || {});
37
37
  const telemetry = new TelemetryCacheInRedis(log, keys, redisClient);
38
- const impressionCountsCache = impressionsMode !== DEBUG ? new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient) : undefined;
39
- const uniqueKeysCache = impressionsMode === NONE ? new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient) : undefined;
38
+ const impressionCountsCache = new ImpressionCountsCacheInRedis(log, keys.buildImpressionsCountKey(), redisClient);
39
+ const uniqueKeysCache = new UniqueKeysCacheInRedis(log, keys.buildUniqueKeysKey(), redisClient);
40
40
 
41
41
  // subscription to Redis connect event in order to emit SDK_READY event on consumer mode
42
42
  redisClient.on('connect', () => {
43
43
  onReadyCb();
44
- if (impressionCountsCache) impressionCountsCache.start();
45
- if (uniqueKeysCache) uniqueKeysCache.start();
44
+ impressionCountsCache.start();
45
+ uniqueKeysCache.start();
46
46
 
47
47
  // Synchronize config
48
48
  telemetry.recordConfig();
@@ -60,10 +60,10 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
60
60
  // When using REDIS we should:
61
61
  // 1- Disconnect from the storage
62
62
  destroy(): Promise<void> {
63
- let promises = [];
64
- if (impressionCountsCache) promises.push(impressionCountsCache.stop());
65
- if (uniqueKeysCache) promises.push(uniqueKeysCache.stop());
66
- return Promise.all(promises).then(() => { redisClient.disconnect(); });
63
+ return Promise.all([
64
+ impressionCountsCache.stop(),
65
+ uniqueKeysCache.stop()
66
+ ]).then(() => { redisClient.disconnect(); });
67
67
  // @TODO check that caches works as expected when redisClient is disconnected
68
68
  }
69
69
  };
@@ -8,7 +8,7 @@ import { EventsCachePluggable } from './EventsCachePluggable';
8
8
  import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
9
9
  import { isObject } from '../../utils/lang';
10
10
  import { getStorageHash, validatePrefix } from '../KeyBuilder';
11
- import { CONSUMER_PARTIAL_MODE, DEBUG, NONE, STORAGE_PLUGGABLE } from '../../utils/constants';
11
+ import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
12
12
  import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
13
13
  import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
14
14
  import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
@@ -63,35 +63,31 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
63
63
  const prefix = validatePrefix(options.prefix);
64
64
 
65
65
  function PluggableStorageFactory(params: IStorageFactoryParams): IStorageAsync {
66
- const { onReadyCb, settings, settings: { log, mode, sync: { impressionsMode }, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
66
+ const { onReadyCb, settings, settings: { log, mode, scheduler: { impressionsQueueSize, eventsQueueSize } } } = params;
67
67
  const metadata = metadataBuilder(settings);
68
68
  const keys = new KeyBuilderSS(prefix, metadata);
69
69
  const wrapper = wrapperAdapter(log, options.wrapper);
70
70
 
71
- const isSyncronizer = mode === undefined; // If mode is not defined, the synchronizer is running
71
+ const isSynchronizer = mode === undefined; // If mode is not defined, the synchronizer is running
72
72
  const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
73
73
 
74
- const telemetry = shouldRecordTelemetry(params) || isSyncronizer ?
74
+ const telemetry = shouldRecordTelemetry(params) || isSynchronizer ?
75
75
  isPartialConsumer ?
76
76
  new TelemetryCacheInMemory() :
77
77
  new TelemetryCachePluggable(log, keys, wrapper) :
78
78
  undefined;
79
79
 
80
- const impressionCountsCache = impressionsMode !== DEBUG || isSyncronizer ?
81
- isPartialConsumer ?
82
- new ImpressionCountsCacheInMemory() :
83
- new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper) :
84
- undefined;
80
+ const impressionCountsCache = isPartialConsumer ?
81
+ new ImpressionCountsCacheInMemory() :
82
+ new ImpressionCountsCachePluggable(log, keys.buildImpressionsCountKey(), wrapper);
85
83
 
86
- const uniqueKeysCache = impressionsMode === NONE || isSyncronizer ?
87
- isPartialConsumer ?
88
- settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
89
- new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper) :
90
- undefined;
84
+ const uniqueKeysCache = isPartialConsumer ?
85
+ settings.core.key === undefined ? new UniqueKeysCacheInMemory() : new UniqueKeysCacheInMemoryCS() :
86
+ new UniqueKeysCachePluggable(log, keys.buildUniqueKeysKey(), wrapper);
91
87
 
92
88
  // Connects to wrapper and emits SDK_READY event on main client
93
89
  const connectPromise = wrapper.connect().then(() => {
94
- if (isSyncronizer) {
90
+ if (isSynchronizer) {
95
91
  // In standalone or producer mode, clear storage if SDK key or feature flag filter has changed
96
92
  return wrapper.get(keys.buildHashKey()).then((hash) => {
97
93
  const currentHash = getStorageHash(settings);
@@ -106,8 +102,8 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
106
102
  });
107
103
  } else {
108
104
  // Start periodic flush of async storages if not running synchronizer (producer mode)
109
- if (impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
110
- if (uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
105
+ if ((impressionCountsCache as ImpressionCountsCachePluggable).start) (impressionCountsCache as ImpressionCountsCachePluggable).start();
106
+ if ((uniqueKeysCache as UniqueKeysCachePluggable).start) (uniqueKeysCache as UniqueKeysCachePluggable).start();
111
107
  if (telemetry && (telemetry as ITelemetryCacheAsync).recordConfig) (telemetry as ITelemetryCacheAsync).recordConfig();
112
108
 
113
109
  onReadyCb();
@@ -129,9 +125,9 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
129
125
 
130
126
  // Stop periodic flush and disconnect the underlying storage
131
127
  destroy() {
132
- return Promise.all(isSyncronizer ? [] : [
133
- impressionCountsCache && (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
134
- uniqueKeysCache && (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
128
+ return Promise.all(isSynchronizer ? [] : [
129
+ (impressionCountsCache as ImpressionCountsCachePluggable).stop && (impressionCountsCache as ImpressionCountsCachePluggable).stop(),
130
+ (uniqueKeysCache as UniqueKeysCachePluggable).stop && (uniqueKeysCache as UniqueKeysCachePluggable).stop(),
135
131
  ]).then(() => wrapper.disconnect());
136
132
  },
137
133
 
@@ -439,10 +439,10 @@ export interface IStorageBase<
439
439
  splits: TSplitsCache,
440
440
  segments: TSegmentsCache,
441
441
  impressions: TImpressionsCache,
442
- impressionCounts?: TImpressionsCountCache,
442
+ impressionCounts: TImpressionsCountCache,
443
443
  events: TEventsCache,
444
444
  telemetry?: TTelemetryCache,
445
- uniqueKeys?: TUniqueKeysCache,
445
+ uniqueKeys: TUniqueKeysCache,
446
446
  destroy(): void | Promise<void>,
447
447
  shared?: (matchingKey: string, onReadyCb: (error?: any) => void) => this
448
448
  }
@@ -39,8 +39,6 @@ export function impressionCountsSubmitterFactory(params: ISdkFactoryContextSync)
39
39
  storage: { impressionCounts }
40
40
  } = params;
41
41
 
42
- if (impressionCounts) {
43
- // retry impressions counts only once.
44
- return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
45
- }
42
+ // retry impressions counts only once.
43
+ return submitterFactory(log, postTestImpressionsCount, impressionCounts, IMPRESSIONS_COUNT_RATE, 'impression counts', fromImpressionCountsCollector, 1);
46
44
  }
@@ -10,13 +10,12 @@ export function submitterManagerFactory(params: ISdkFactoryContextSync): ISubmit
10
10
 
11
11
  const submitters = [
12
12
  impressionsSubmitterFactory(params),
13
- eventsSubmitterFactory(params)
13
+ eventsSubmitterFactory(params),
14
+ impressionCountsSubmitterFactory(params),
15
+ uniqueKeysSubmitterFactory(params)
14
16
  ];
15
17
 
16
- const impressionCountsSubmitter = impressionCountsSubmitterFactory(params);
17
- if (impressionCountsSubmitter) submitters.push(impressionCountsSubmitter);
18
18
  const telemetrySubmitter = telemetrySubmitterFactory(params);
19
- if (params.storage.uniqueKeys) submitters.push(uniqueKeysSubmitterFactory(params));
20
19
 
21
20
  return {
22
21
  // `onlyTelemetry` true if SDK is created with userConsent not GRANTED
@@ -19,10 +19,10 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
19
19
  const isClientSide = key !== undefined;
20
20
  const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
21
21
 
22
- const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys!, UNIQUE_KEYS_RATE, DATA_NAME);
22
+ const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys, UNIQUE_KEYS_RATE, DATA_NAME);
23
23
 
24
24
  // register unique keys submitter to be executed when uniqueKeys cache is full
25
- uniqueKeys!.setOnFullQueueCb(() => {
25
+ uniqueKeys.setOnFullQueueCb(() => {
26
26
  if (syncTask.isRunning()) {
27
27
  log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
28
28
  syncTask.execute();
@@ -33,4 +33,3 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
33
33
 
34
34
  return syncTask;
35
35
  }
36
-
@@ -9,16 +9,11 @@ import SplitIO from '../../types/splitio';
9
9
 
10
10
  /**
11
11
  * Impressions tracker stores impressions in cache and pass them to the listener and integrations manager if provided.
12
- *
13
- * @param impressionsCache - cache to save impressions
14
- * @param metadata - runtime metadata (ip, hostname and version)
15
- * @param impressionListener - optional impression listener
16
- * @param integrationsManager - optional integrations manager
17
- * @param strategy - strategy for impressions tracking.
18
12
  */
19
13
  export function impressionsTrackerFactory(
20
14
  settings: ISettings,
21
15
  impressionsCache: IImpressionsCacheBase,
16
+ noneStrategy: IStrategy,
22
17
  strategy: IStrategy,
23
18
  whenInit: (cb: () => void) => void,
24
19
  integrationsManager?: IImpressionsHandler,
@@ -28,40 +23,44 @@ export function impressionsTrackerFactory(
28
23
  const { log, impressionListener, runtime: { ip, hostname }, version } = settings;
29
24
 
30
25
  return {
31
- track(impressions: SplitIO.ImpressionDTO[], attributes?: SplitIO.Attributes) {
26
+ track(impressions: [impression: SplitIO.ImpressionDTO, track?: boolean][], attributes?: SplitIO.Attributes) {
32
27
  if (settings.userConsent === CONSENT_DECLINED) return;
33
28
 
34
- const impressionsCount = impressions.length;
35
- const { impressionsToStore, impressionsToListener, deduped } = strategy.process(impressions);
29
+ const impressionsToStore = impressions.filter(([impression, track]) => {
30
+ return track === false ?
31
+ noneStrategy.process(impression) :
32
+ strategy.process(impression);
33
+ });
36
34
 
37
- const impressionsToListenerCount = impressionsToListener.length;
35
+ const impressionsLength = impressions.length;
36
+ const impressionsToStoreLength = impressionsToStore.length;
38
37
 
39
- if (impressionsToStore.length > 0) {
40
- const res = impressionsCache.track(impressionsToStore);
38
+ if (impressionsToStoreLength) {
39
+ const res = impressionsCache.track(impressionsToStore.map((item) => item[0]));
41
40
 
42
41
  // If we're on an async storage, handle error and log it.
43
42
  if (thenable(res)) {
44
43
  res.then(() => {
45
- log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
44
+ log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsLength]);
46
45
  }).catch(err => {
47
- log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
46
+ log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsLength, err]);
48
47
  });
49
48
  } else {
50
49
  // Record when impressionsCache is sync only (standalone mode)
51
50
  // @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
52
51
  if (telemetryCache) {
53
- (telemetryCache as ITelemetryCacheSync).recordImpressionStats(QUEUED, impressionsToStore.length);
54
- (telemetryCache as ITelemetryCacheSync).recordImpressionStats(DEDUPED, deduped);
52
+ (telemetryCache as ITelemetryCacheSync).recordImpressionStats(QUEUED, impressionsToStoreLength);
53
+ (telemetryCache as ITelemetryCacheSync).recordImpressionStats(DEDUPED, impressionsLength - impressionsToStoreLength);
55
54
  }
56
55
  }
57
56
  }
58
57
 
59
58
  // @TODO next block might be handled by the integration manager. In that case, the metadata object doesn't need to be passed in the constructor
60
59
  if (impressionListener || integrationsManager) {
61
- for (let i = 0; i < impressionsToListenerCount; i++) {
60
+ for (let i = 0; i < impressionsLength; i++) {
62
61
  const impressionData: SplitIO.ImpressionData = {
63
62
  // copy of impression, to avoid unexpected behaviour if modified by integrations or impressionListener
64
- impression: objectAssign({}, impressionsToListener[i]),
63
+ impression: objectAssign({}, impressions[i][0]),
65
64
  attributes,
66
65
  ip,
67
66
  hostname,
@@ -6,23 +6,16 @@ import { IStrategy } from '../types';
6
6
  * Debug strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
7
7
  *
8
8
  * @param impressionsObserver - impression observer. Previous time (pt property) is included in impression instances
9
- * @returns IStrategyResult
9
+ * @returns Debug strategy
10
10
  */
11
11
  export function strategyDebugFactory(
12
12
  impressionsObserver: IImpressionObserver
13
13
  ): IStrategy {
14
14
 
15
15
  return {
16
- process(impressions: SplitIO.ImpressionDTO[]) {
17
- impressions.forEach((impression) => {
18
- // Adds previous time if it is enabled
19
- impression.pt = impressionsObserver.testAndSet(impression);
20
- });
21
- return {
22
- impressionsToStore: impressions,
23
- impressionsToListener: impressions,
24
- deduped: 0
25
- };
16
+ process(impression: SplitIO.ImpressionDTO) {
17
+ impression.pt = impressionsObserver.testAndSet(impression);
18
+ return true;
26
19
  }
27
20
  };
28
21
  }
@@ -5,30 +5,24 @@ import { IStrategy, IUniqueKeysTracker } from '../types';
5
5
  /**
6
6
  * None strategy for impressions tracker.
7
7
  *
8
- * @param impressionsCounter - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
8
+ * @param impressionCounts - cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
9
9
  * @param uniqueKeysTracker - unique keys tracker in charge of tracking the unique keys per split.
10
- * @returns IStrategyResult
10
+ * @returns None strategy
11
11
  */
12
12
  export function strategyNoneFactory(
13
- impressionsCounter: IImpressionCountsCacheBase,
13
+ impressionCounts: IImpressionCountsCacheBase,
14
14
  uniqueKeysTracker: IUniqueKeysTracker
15
15
  ): IStrategy {
16
16
 
17
17
  return {
18
- process(impressions: SplitIO.ImpressionDTO[]) {
19
- impressions.forEach((impression) => {
20
- const now = Date.now();
21
- // Increments impression counter per featureName
22
- impressionsCounter.track(impression.feature, now, 1);
23
- // Keep track by unique key
24
- uniqueKeysTracker.track(impression.keyName, impression.feature);
25
- });
26
-
27
- return {
28
- impressionsToStore: [],
29
- impressionsToListener: impressions,
30
- deduped: 0
31
- };
18
+ process(impression: SplitIO.ImpressionDTO) {
19
+ const now = Date.now();
20
+ // Increments impression counter per featureName
21
+ impressionCounts.track(impression.feature, now, 1);
22
+ // Keep track by unique key
23
+ uniqueKeysTracker.track(impression.keyName, impression.feature);
24
+ // Do not store impressions
25
+ return false;
32
26
  }
33
27
  };
34
28
  }