@splitsoftware/splitio-commons 1.6.2-rc.1 → 1.6.2-rc.10

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 (197) hide show
  1. package/CHANGES.txt +4 -0
  2. package/cjs/consent/sdkUserConsent.js +2 -2
  3. package/cjs/evaluator/index.js +15 -16
  4. package/cjs/integrations/ga/GaToSplit.js +8 -5
  5. package/cjs/sdkClient/client.js +19 -7
  6. package/cjs/sdkClient/sdkClient.js +3 -1
  7. package/cjs/sdkFactory/index.js +15 -6
  8. package/cjs/sdkManager/index.js +3 -11
  9. package/cjs/services/splitApi.js +6 -6
  10. package/cjs/storages/AbstractSplitsCacheAsync.js +8 -10
  11. package/cjs/storages/AbstractSplitsCacheSync.js +8 -10
  12. package/cjs/storages/KeyBuilderSS.js +54 -9
  13. package/cjs/storages/dataLoader.js +1 -1
  14. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
  15. package/cjs/storages/inLocalStorage/index.js +5 -1
  16. package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  17. package/cjs/storages/inMemory/InMemoryStorage.js +6 -2
  18. package/cjs/storages/inMemory/InMemoryStorageCS.js +6 -2
  19. package/cjs/storages/inMemory/SplitsCacheInMemory.js +7 -10
  20. package/cjs/storages/inMemory/TelemetryCacheInMemory.js +10 -5
  21. package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +73 -0
  22. package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +78 -0
  23. package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
  24. package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +50 -0
  25. package/cjs/storages/inRedis/SplitsCacheInRedis.js +15 -9
  26. package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  27. package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +59 -0
  28. package/cjs/storages/inRedis/constants.js +4 -1
  29. package/cjs/storages/inRedis/index.js +17 -2
  30. package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +43 -0
  31. package/cjs/storages/pluggable/SplitsCachePluggable.js +14 -9
  32. package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
  33. package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +50 -0
  34. package/cjs/storages/pluggable/index.js +42 -17
  35. package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  36. package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
  37. package/cjs/sync/submitters/telemetrySubmitter.js +8 -4
  38. package/cjs/sync/submitters/uniqueKeysSubmitter.js +16 -59
  39. package/cjs/trackers/impressionsTracker.js +17 -15
  40. package/cjs/trackers/strategy/strategyNone.js +1 -1
  41. package/cjs/trackers/strategy/strategyOptimized.js +2 -1
  42. package/cjs/trackers/telemetryTracker.js +6 -0
  43. package/cjs/trackers/uniqueKeysTracker.js +11 -42
  44. package/cjs/utils/constants/index.js +3 -2
  45. package/cjs/utils/lang/maps.js +15 -7
  46. package/cjs/utils/redis/RedisMock.js +31 -0
  47. package/cjs/utils/settingsValidation/index.js +0 -3
  48. package/esm/consent/sdkUserConsent.js +2 -2
  49. package/esm/evaluator/index.js +15 -16
  50. package/esm/integrations/ga/GaToSplit.js +8 -5
  51. package/esm/sdkClient/client.js +19 -7
  52. package/esm/sdkClient/sdkClient.js +3 -1
  53. package/esm/sdkFactory/index.js +16 -7
  54. package/esm/sdkManager/index.js +3 -11
  55. package/esm/services/splitApi.js +6 -6
  56. package/esm/storages/AbstractSplitsCacheAsync.js +8 -10
  57. package/esm/storages/AbstractSplitsCacheSync.js +8 -10
  58. package/esm/storages/KeyBuilderSS.js +50 -8
  59. package/esm/storages/dataLoader.js +1 -1
  60. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -7
  61. package/esm/storages/inLocalStorage/index.js +6 -2
  62. package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
  63. package/esm/storages/inMemory/InMemoryStorage.js +8 -4
  64. package/esm/storages/inMemory/InMemoryStorageCS.js +7 -3
  65. package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -10
  66. package/esm/storages/inMemory/TelemetryCacheInMemory.js +9 -5
  67. package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +70 -0
  68. package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +75 -0
  69. package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
  70. package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +47 -0
  71. package/esm/storages/inRedis/SplitsCacheInRedis.js +15 -9
  72. package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
  73. package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +56 -0
  74. package/esm/storages/inRedis/constants.js +3 -0
  75. package/esm/storages/inRedis/index.js +18 -3
  76. package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +40 -0
  77. package/esm/storages/pluggable/SplitsCachePluggable.js +14 -9
  78. package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
  79. package/esm/storages/pluggable/UniqueKeysCachePluggable.js +47 -0
  80. package/esm/storages/pluggable/index.js +43 -18
  81. package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
  82. package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
  83. package/esm/sync/submitters/telemetrySubmitter.js +9 -5
  84. package/esm/sync/submitters/uniqueKeysSubmitter.js +15 -56
  85. package/esm/trackers/impressionsTracker.js +17 -15
  86. package/esm/trackers/strategy/strategyNone.js +1 -1
  87. package/esm/trackers/strategy/strategyOptimized.js +2 -1
  88. package/esm/trackers/telemetryTracker.js +6 -0
  89. package/esm/trackers/uniqueKeysTracker.js +11 -42
  90. package/esm/utils/constants/index.js +1 -0
  91. package/esm/utils/lang/maps.js +15 -7
  92. package/esm/utils/redis/RedisMock.js +28 -0
  93. package/esm/utils/settingsValidation/index.js +0 -3
  94. package/package.json +1 -2
  95. package/src/consent/sdkUserConsent.ts +2 -2
  96. package/src/evaluator/index.ts +14 -15
  97. package/src/integrations/ga/GaToSplit.ts +9 -5
  98. package/src/integrations/types.ts +2 -1
  99. package/src/logger/.DS_Store +0 -0
  100. package/src/sdkClient/client.ts +21 -8
  101. package/src/sdkClient/sdkClient.ts +3 -1
  102. package/src/sdkFactory/index.ts +17 -7
  103. package/src/sdkManager/index.ts +3 -12
  104. package/src/services/splitApi.ts +6 -6
  105. package/src/services/types.ts +2 -2
  106. package/src/storages/AbstractSplitsCacheAsync.ts +13 -15
  107. package/src/storages/AbstractSplitsCacheSync.ts +15 -17
  108. package/src/storages/KeyBuilderSS.ts +61 -9
  109. package/src/storages/dataLoader.ts +1 -1
  110. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +8 -11
  111. package/src/storages/inLocalStorage/index.ts +5 -2
  112. package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +16 -1
  113. package/src/storages/inMemory/InMemoryStorage.ts +7 -4
  114. package/src/storages/inMemory/InMemoryStorageCS.ts +6 -3
  115. package/src/storages/inMemory/SplitsCacheInMemory.ts +10 -14
  116. package/src/storages/inMemory/TelemetryCacheInMemory.ts +10 -6
  117. package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +82 -0
  118. package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +88 -0
  119. package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
  120. package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +53 -0
  121. package/src/storages/inRedis/SplitsCacheInRedis.ts +21 -17
  122. package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
  123. package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +65 -0
  124. package/src/storages/inRedis/constants.ts +3 -0
  125. package/src/storages/inRedis/index.ts +15 -5
  126. package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +47 -0
  127. package/src/storages/pluggable/SplitsCachePluggable.ts +20 -17
  128. package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
  129. package/src/storages/pluggable/UniqueKeysCachePluggable.ts +56 -0
  130. package/src/storages/pluggable/index.ts +44 -19
  131. package/src/storages/types.ts +38 -30
  132. package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +5 -6
  133. package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -2
  134. package/src/sync/submitters/telemetrySubmitter.ts +15 -8
  135. package/src/sync/submitters/types.ts +18 -6
  136. package/src/sync/submitters/uniqueKeysSubmitter.ts +18 -61
  137. package/src/trackers/impressionsTracker.ts +16 -15
  138. package/src/trackers/strategy/strategyNone.ts +1 -1
  139. package/src/trackers/strategy/strategyOptimized.ts +1 -1
  140. package/src/trackers/telemetryTracker.ts +7 -2
  141. package/src/trackers/types.ts +9 -7
  142. package/src/trackers/uniqueKeysTracker.ts +15 -47
  143. package/src/types.ts +0 -1
  144. package/src/utils/constants/index.ts +1 -0
  145. package/src/utils/lang/maps.ts +20 -8
  146. package/src/utils/redis/RedisMock.ts +33 -0
  147. package/src/utils/settingsValidation/index.ts +1 -4
  148. package/types/integrations/types.d.ts +2 -1
  149. package/types/services/types.d.ts +2 -2
  150. package/types/storages/AbstractSplitsCacheAsync.d.ts +7 -6
  151. package/types/storages/AbstractSplitsCacheSync.d.ts +6 -6
  152. package/types/storages/KeyBuilderSS.d.ts +9 -2
  153. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +3 -4
  154. package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +5 -1
  155. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +3 -2
  156. package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -3
  157. package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +35 -0
  158. package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +37 -0
  159. package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
  160. package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +14 -0
  161. package/types/storages/inRedis/SplitsCacheInRedis.d.ts +6 -5
  162. package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -1
  163. package/types/storages/inRedis/constants.d.ts +3 -0
  164. package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +15 -0
  165. package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +14 -0
  166. package/types/storages/pluggable/SplitsCachePluggable.d.ts +6 -5
  167. package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
  168. package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +14 -0
  169. package/types/storages/types.d.ts +35 -35
  170. package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
  171. package/types/sync/submitters/telemetrySubmitter.d.ts +1 -1
  172. package/types/sync/submitters/types.d.ts +12 -6
  173. package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -14
  174. package/types/trackers/types.d.ts +9 -11
  175. package/types/trackers/uniqueKeysTracker.d.ts +3 -3
  176. package/types/types.d.ts +0 -1
  177. package/types/utils/constants/index.d.ts +1 -0
  178. package/types/utils/lang/maps.d.ts +6 -2
  179. package/types/utils/redis/RedisMock.d.ts +4 -0
  180. package/types/utils/settingsValidation/index.d.ts +0 -1
  181. package/types/sdkClient/types.d.ts +0 -18
  182. package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
  183. package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
  184. package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
  185. package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
  186. package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
  187. package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
  188. package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
  189. package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
  190. package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
  191. package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
  192. package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
  193. package/types/sync/syncTaskComposite.d.ts +0 -5
  194. package/types/trackers/filter/bloomFilter.d.ts +0 -10
  195. package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
  196. package/types/trackers/filter/types.d.ts +0 -5
  197. package/types/utils/timeTracker/index.d.ts +0 -70
@@ -2,7 +2,7 @@ import { forOwn } from '../../../utils/lang';
2
2
  import { IReadinessManager } from '../../../readiness/types';
3
3
  import { ISplitsCacheSync } from '../../../storages/types';
4
4
  import { ISplitsParser } from '../splitsParser/types';
5
- import { ISplitPartial } from '../../../dtos/types';
5
+ import { ISplit, ISplitPartial } from '../../../dtos/types';
6
6
  import { syncTaskFactory } from '../../syncTask';
7
7
  import { ISyncTask } from '../../types';
8
8
  import { ISettings } from '../../../types';
@@ -24,7 +24,7 @@ export function fromObjectUpdaterFactory(
24
24
  let startingUp = true;
25
25
 
26
26
  return function objectUpdater() {
27
- const splits: [string, string][] = [];
27
+ const splits: [string, ISplit][] = [];
28
28
  let loadError = null;
29
29
  let splitsMock: false | Record<string, ISplitPartial> = {};
30
30
  try {
@@ -38,9 +38,8 @@ export function fromObjectUpdaterFactory(
38
38
  log.debug(SYNC_OFFLINE_DATA, [JSON.stringify(splitsMock)]);
39
39
 
40
40
  forOwn(splitsMock, function (val, name) {
41
- splits.push([
42
- name,
43
- JSON.stringify({
41
+ splits.push([ // @ts-ignore Split changeNumber and seed is undefined in localhost mode
42
+ name, {
44
43
  name,
45
44
  status: 'ACTIVE',
46
45
  killed: false,
@@ -49,7 +48,7 @@ export function fromObjectUpdaterFactory(
49
48
  conditions: val.conditions || [],
50
49
  configurations: val.configurations,
51
50
  trafficTypeName: val.trafficTypeName
52
- })
51
+ }
53
52
  ]);
54
53
  });
55
54
 
@@ -40,7 +40,7 @@ export function parseSegments({ conditions }: ISplit): ISet<string> {
40
40
  }
41
41
 
42
42
  interface ISplitMutations {
43
- added: [string, string][],
43
+ added: [string, ISplit][],
44
44
  removed: string[],
45
45
  segments: string[]
46
46
  }
@@ -54,7 +54,7 @@ export function computeSplitsMutation(entries: ISplit[]): ISplitMutations {
54
54
  const segments = new _Set<string>();
55
55
  const computed = entries.reduce((accum, split) => {
56
56
  if (split.status === 'ACTIVE') {
57
- accum.added.push([split.name, JSON.stringify(split)]);
57
+ accum.added.push([split.name, split]);
58
58
 
59
59
  parseSegments(split).forEach((segmentName: string) => {
60
60
  segments.add(segmentName);
@@ -1,7 +1,7 @@
1
1
  import { ISegmentsCacheSync, ISplitsCacheSync, ITelemetryCacheSync } from '../../storages/types';
2
2
  import { submitterFactory, firstPushWindowDecorator } from './submitter';
3
3
  import { TelemetryUsageStatsPayload, TelemetryConfigStatsPayload, TelemetryConfigStats } from './types';
4
- import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, DEBUG_ENUM, OPTIMIZED_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
4
+ import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, NONE, DEBUG_ENUM, OPTIMIZED_ENUM, NONE_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
5
5
  import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
6
6
  import { ConsentStatus, ISettings, SDKMode } from '../../types';
7
7
  import { base } from '../../utils/settingsValidation';
@@ -9,11 +9,12 @@ import { usedKeysMap } from '../../utils/inputValidation/apiKey';
9
9
  import { timer } from '../../utils/timeTracker/timer';
10
10
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
11
11
  import { objectAssign } from '../../utils/lang/objectAssign';
12
+ import { isStorageSync } from '../../trackers/impressionObserver/utils';
12
13
 
13
14
  /**
14
15
  * Converts data from telemetry cache into /metrics/usage request payload.
15
16
  */
16
- export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits: ISplitsCacheSync, segments: ISegmentsCacheSync) {
17
+ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, splits?: ISplitsCacheSync, segments?: ISegmentsCacheSync) {
17
18
  return {
18
19
  isEmpty() { return false; }, // There is always data in telemetry cache
19
20
  clear() { }, // No-op
@@ -31,9 +32,9 @@ export function telemetryCacheStatsAdapter(telemetry: ITelemetryCacheSync, split
31
32
  iQ: telemetry.getImpressionStats(QUEUED),
32
33
  iDe: telemetry.getImpressionStats(DEDUPED),
33
34
  iDr: telemetry.getImpressionStats(DROPPED),
34
- spC: splits.getSplitNames().length,
35
- seC: segments.getRegisteredSegments().length,
36
- skC: segments.getKeysCount(),
35
+ spC: splits && splits.getSplitNames().length,
36
+ seC: segments && segments.getRegisteredSegments().length,
37
+ skC: segments && segments.getKeysCount(),
37
38
  sL: telemetry.getSessionLength(),
38
39
  eQ: telemetry.getEventStats(QUEUED),
39
40
  eD: telemetry.getEventStats(DROPPED),
@@ -52,8 +53,9 @@ const OPERATION_MODE_MAP = {
52
53
 
53
54
  const IMPRESSIONS_MODE_MAP = {
54
55
  [OPTIMIZED]: OPTIMIZED_ENUM,
55
- [DEBUG]: DEBUG_ENUM
56
- } as Record<ISettings['sync']['impressionsMode'], (0 | 1)>;
56
+ [DEBUG]: DEBUG_ENUM,
57
+ [NONE]: NONE_ENUM
58
+ } as Record<ISettings['sync']['impressionsMode'], (0 | 1 | 2)>;
57
59
 
58
60
  const USER_CONSENT_MAP = {
59
61
  [CONSENT_UNKNOWN]: 1,
@@ -136,7 +138,12 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
136
138
  const startTime = timer(now);
137
139
 
138
140
  const submitter = firstPushWindowDecorator(
139
- submitterFactory(log, splitApi.postMetricsUsage, telemetryCacheStatsAdapter(telemetry, splits, segments), telemetryRefreshRate, 'telemetry stats', undefined, 0, true),
141
+ submitterFactory(
142
+ log, splitApi.postMetricsUsage,
143
+ // @TODO cannot provide splits and segments cache if they are async, because `submitterFactory` expects a sync storage source
144
+ isStorageSync(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry),
145
+ telemetryRefreshRate, 'telemetry stats', undefined, 0, true
146
+ ),
140
147
  telemetryRefreshRate
141
148
  );
142
149
 
@@ -1,5 +1,7 @@
1
+ /* eslint-disable no-use-before-define */
1
2
  import { IMetadata } from '../../dtos/types';
2
3
  import { SplitIO } from '../../types';
4
+ import { IMap } from '../../utils/lang/maps';
3
5
  import { ISyncTask } from '../types';
4
6
 
5
7
  export type ImpressionsPayload = {
@@ -82,6 +84,12 @@ export type StoredEventWithMetadata = {
82
84
  e: SplitIO.EventData
83
85
  }
84
86
 
87
+ export type MultiMethodLatencies = IMap<string, MethodLatencies>
88
+
89
+ export type MultiMethodExceptions = IMap<string, MethodExceptions>
90
+
91
+ export type MultiConfigs = IMap<string, TelemetryConfigStats>
92
+
85
93
  /**
86
94
  * Telemetry usage stats
87
95
  */
@@ -133,11 +141,15 @@ export type StreamingEvent = {
133
141
  t: number, // timestamp
134
142
  }
135
143
 
144
+ // 'telemetry.latencias' and 'telemetry.exceptions' Redis/Pluggable keys
145
+ export type TelemetryUsageStats = {
146
+ mL?: MethodLatencies, // clientMethodLatencies
147
+ mE?: MethodExceptions, // methodExceptions
148
+ }
149
+
136
150
  // 'metrics/usage' JSON request body
137
- export type TelemetryUsageStatsPayload = {
151
+ export type TelemetryUsageStatsPayload = TelemetryUsageStats & {
138
152
  lS: LastSync, // lastSynchronization
139
- mL: MethodLatencies, // clientMethodLatencies
140
- mE: MethodExceptions, // methodExceptions
141
153
  hE: HttpErrors, // httpErrors
142
154
  hL: HttpLatencies, // httpLatencies
143
155
  tR: number, // tokenRefreshes
@@ -145,9 +157,9 @@ export type TelemetryUsageStatsPayload = {
145
157
  iQ: number, // impressionsQueued
146
158
  iDe: number, // impressionsDeduped
147
159
  iDr: number, // impressionsDropped
148
- spC: number, // splitCount
149
- seC: number, // segmentCount
150
- skC: number, // segmentKeyCount
160
+ spC?: number, // splitCount
161
+ seC?: number, // segmentCount
162
+ skC?: number, // segmentKeyCount
151
163
  sL?: number, // sessionLengthMs
152
164
  eQ: number, // eventsQueued
153
165
  eD: number, // eventsDropped
@@ -1,63 +1,9 @@
1
+ import { SUBMITTERS_PUSH_FULL_QUEUE } from '../../logger/constants';
1
2
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
2
- import { ISet, setToArray } from '../../utils/lang/sets';
3
3
  import { submitterFactory } from './submitter';
4
- import { UniqueKeysPayloadCs, UniqueKeysPayloadSs } from './types';
5
4
 
6
- /**
7
- * Invert keys for feature to features for key
8
- */
9
- function invertUniqueKeys(uniqueKeys: { [featureName: string]: ISet<string> }): { [key: string]: string[] } {
10
- const featureNames = Object.keys(uniqueKeys);
11
- const inverted: { [key: string]: string[] } = {};
12
- for (let i = 0; i < featureNames.length; i++) {
13
- const featureName = featureNames[i];
14
- const featureKeys = setToArray(uniqueKeys[featureName]);
15
- for (let j = 0; j< featureKeys.length; j++) {
16
- const featureKey = featureKeys[j];
17
- if (!inverted[featureKey]) inverted[featureKey] = [];
18
- inverted[featureKey].push(featureName);
19
- }
20
- }
21
- return inverted;
22
- }
23
-
24
- /**
25
- * Converts `uniqueKeys` data from cache into request payload for CS.
26
- */
27
- export function fromUniqueKeysCollectorCs(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadCs {
28
- const payload = [];
29
- const featuresPerKey = invertUniqueKeys(uniqueKeys);
30
- const keys = Object.keys(featuresPerKey);
31
- for (let k = 0; k < keys.length; k++) {
32
- const key = keys[k];
33
- const uniqueKeysPayload = {
34
- k: key,
35
- fs: featuresPerKey[key]
36
- };
37
-
38
- payload.push(uniqueKeysPayload);
39
- }
40
- return { keys: payload };
41
- }
42
-
43
- /**
44
- * Converts `uniqueKeys` data from cache into request payload for SS.
45
- */
46
- export function fromUniqueKeysCollectorSs(uniqueKeys: { [featureName: string]: ISet<string> }): UniqueKeysPayloadSs {
47
- const payload = [];
48
- const featureNames = Object.keys(uniqueKeys);
49
- for (let i = 0; i < featureNames.length; i++) {
50
- const featureName = featureNames[i];
51
- const featureKeys = setToArray(uniqueKeys[featureName]);
52
- const uniqueKeysPayload = {
53
- f: featureName,
54
- ks: featureKeys
55
- };
56
-
57
- payload.push(uniqueKeysPayload);
58
- }
59
- return { keys: payload };
60
- }
5
+ const DATA_NAME = 'unique keys';
6
+ const UNIQUE_KEYS_RATE = 900000; // 15 minutes
61
7
 
62
8
  /**
63
9
  * Submitter that periodically posts impression counts
@@ -65,15 +11,26 @@ export function fromUniqueKeysCollectorSs(uniqueKeys: { [featureName: string]: I
65
11
  export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
66
12
 
67
13
  const {
68
- settings: { log, scheduler: { uniqueKeysRefreshRate }, core: {key}},
14
+ settings: { log, core: { key } },
69
15
  splitApi: { postUniqueKeysBulkCs, postUniqueKeysBulkSs },
70
16
  storage: { uniqueKeys }
71
17
  } = params;
72
-
18
+
73
19
  const isClientSide = key !== undefined;
74
20
  const postUniqueKeysBulk = isClientSide ? postUniqueKeysBulkCs : postUniqueKeysBulkSs;
75
- const fromUniqueKeysCollector = isClientSide ? fromUniqueKeysCollectorCs : fromUniqueKeysCollectorSs;
76
21
 
77
- return submitterFactory(log, postUniqueKeysBulk, uniqueKeys!, uniqueKeysRefreshRate, 'unique keys', fromUniqueKeysCollector);
22
+ const syncTask = submitterFactory(log, postUniqueKeysBulk, uniqueKeys!, UNIQUE_KEYS_RATE, DATA_NAME);
23
+
24
+ // register unique keys submitter to be executed when uniqueKeys cache is full
25
+ uniqueKeys!.setOnFullQueueCb(() => {
26
+ if (syncTask.isRunning()) {
27
+ log.info(SUBMITTERS_PUSH_FULL_QUEUE, [DATA_NAME]);
28
+ syncTask.execute();
29
+ }
30
+ // If submitter is stopped (e.g., user consent declined or unknown, or app state offline), we don't send the data.
31
+ // Data will be sent when submitter is resumed.
32
+ });
33
+
34
+ return syncTask;
78
35
  }
79
36
 
@@ -30,26 +30,27 @@ export function impressionsTrackerFactory(
30
30
  if (settings.userConsent === CONSENT_DECLINED) return;
31
31
 
32
32
  const impressionsCount = impressions.length;
33
-
34
33
  const { impressionsToStore, impressionsToListener, deduped } = strategy.process(impressions);
35
34
 
36
35
  const impressionsToListenerCount = impressionsToListener.length;
37
36
 
38
- const res = impressionsCache.track(impressionsToStore);
37
+ if ( impressionsToStore.length>0 ){
38
+ const res = impressionsCache.track(impressionsToStore);
39
39
 
40
- // If we're on an async storage, handle error and log it.
41
- if (thenable(res)) {
42
- res.then(() => {
43
- log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
44
- }).catch(err => {
45
- log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
46
- });
47
- } else {
48
- // Record when impressionsCache is sync only (standalone mode)
49
- // @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
50
- if (telemetryCache) {
51
- (telemetryCache as ITelemetryCacheSync).recordImpressionStats(QUEUED, impressionsToStore.length);
52
- (telemetryCache as ITelemetryCacheSync).recordImpressionStats(DEDUPED, deduped);
40
+ // If we're on an async storage, handle error and log it.
41
+ if (thenable(res)) {
42
+ res.then(() => {
43
+ log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
44
+ }).catch(err => {
45
+ log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
46
+ });
47
+ } else {
48
+ // Record when impressionsCache is sync only (standalone mode)
49
+ // @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
50
+ if (telemetryCache) {
51
+ (telemetryCache as ITelemetryCacheSync).recordImpressionStats(QUEUED, impressionsToStore.length);
52
+ (telemetryCache as ITelemetryCacheSync).recordImpressionStats(DEDUPED, deduped);
53
+ }
53
54
  }
54
55
  }
55
56
 
@@ -21,7 +21,7 @@ export function strategyNoneFactory(
21
21
  // Increments impression counter per featureName
22
22
  impressionsCounter.track(impression.feature, now, 1);
23
23
  // Keep track by unique key
24
- uniqueKeysTracker.track(impression.feature, impression.keyName);
24
+ uniqueKeysTracker.track(impression.keyName, impression.feature);
25
25
  });
26
26
 
27
27
  return {
@@ -25,7 +25,7 @@ export function strategyOptimizedFactory(
25
25
  const now = Date.now();
26
26
 
27
27
  // Increments impression counter per featureName
28
- impressionsCounter.track(impression.feature, now, 1);
28
+ if (impression.pt) impressionsCounter.track(impression.feature, now, 1);
29
29
 
30
30
  // Checks if the impression should be added in queue to be sent
31
31
  if (!impression.pt || impression.pt < truncateTimeFrame(now)) {
@@ -48,6 +48,10 @@ export function telemetryTrackerFactory(
48
48
  });
49
49
  if (e === TOKEN_REFRESH) (telemetryCache as ITelemetryCacheSync).recordTokenRefreshes();
50
50
  }
51
+ },
52
+ addTag(tag: string) {
53
+ // @ts-ignore
54
+ if (telemetryCache.addTag) telemetryCache.addTag(tag);
51
55
  }
52
56
  };
53
57
 
@@ -56,8 +60,9 @@ export function telemetryTrackerFactory(
56
60
  return {
57
61
  trackEval: noopTrack,
58
62
  trackHttp: noopTrack,
59
- sessionLength: () => { },
60
- streamingEvent: () => { },
63
+ sessionLength() { },
64
+ streamingEvent() { },
65
+ addTag() { }
61
66
  };
62
67
  }
63
68
  }
@@ -2,7 +2,6 @@ import { SplitIO, ImpressionDTO } from '../types';
2
2
  import { StreamingEventType, Method, OperationType } from '../sync/submitters/types';
3
3
  import { IEventsCacheBase } from '../storages/types';
4
4
  import { NetworkError } from '../services/types';
5
- import { ISet } from '../utils/lang/sets';
6
5
 
7
6
  /** Events tracker */
8
7
 
@@ -42,12 +41,17 @@ export interface ITelemetryTracker {
42
41
  * Records streaming event
43
42
  */
44
43
  streamingEvent(e: StreamingEventType | AUTH_REJECTION, d?: number): void
44
+ /**
45
+ * Records tag
46
+ */
47
+ addTag(tag: string): void
45
48
  }
46
49
 
47
50
  export interface IFilterAdapter {
48
- add(featureName: string, key: string): boolean;
49
- contains(featureName: string, key: string): boolean;
51
+ add(key: string, featureName: string): boolean;
52
+ contains(key: string, featureName: string): boolean;
50
53
  clear(): void;
54
+ refreshRate?: number;
51
55
  }
52
56
 
53
57
  export interface IImpressionSenderAdapter {
@@ -57,10 +61,8 @@ export interface IImpressionSenderAdapter {
57
61
 
58
62
  /** Unique keys tracker */
59
63
  export interface IUniqueKeysTracker {
60
- track(featureName: string, key: string): void;
61
- pop(toMerge?: { [featureName: string]: ISet<string> }): { [featureName: string]: ISet<string>; };
62
- clear(): void;
63
- isEmpty(): boolean;
64
+ stop(): void;
65
+ track(key: string, featureName: string): void;
64
66
  }
65
67
 
66
68
  export interface IStrategyResult {
@@ -1,6 +1,6 @@
1
1
  import { LOG_PREFIX_UNIQUE_KEYS_TRACKER } from '../logger/constants';
2
2
  import { ILogger } from '../logger/types';
3
- import { ISet, _Set } from '../utils/lang/sets';
3
+ import { IUniqueKeysCacheBase } from '../storages/types';
4
4
  import { IFilterAdapter, IUniqueKeysTracker } from './types';
5
5
 
6
6
  const noopFilterAdapter = {
@@ -9,70 +9,38 @@ const noopFilterAdapter = {
9
9
  clear() {}
10
10
  };
11
11
 
12
- const DEFAULT_CACHE_SIZE = 30000;
13
12
  /**
14
13
  * Trackes uniques keys
15
14
  * Unique Keys Tracker will be in charge of checking if the MTK was already sent to the BE in the last period
16
15
  * or schedule to be sent; if not it will be added in an internal cache and sent in the next post.
17
16
  *
18
17
  * @param log Logger instance
18
+ * @param uniqueKeysCache cache to save unique keys
19
19
  * @param filterAdapter filter adapter
20
- * @param cacheSize optional internal cache size
21
- * @param maxBulkSize optional max MTKs bulk size
22
20
  */
23
21
  export function uniqueKeysTrackerFactory(
24
22
  log: ILogger,
23
+ uniqueKeysCache: IUniqueKeysCacheBase,
25
24
  filterAdapter: IFilterAdapter = noopFilterAdapter,
26
- cacheSize = DEFAULT_CACHE_SIZE,
27
- // @TODO
28
- // maxBulkSize: number = 5000,
29
25
  ): IUniqueKeysTracker {
30
-
31
- let uniqueKeysTracker: { [featureName: string]: ISet<string> } = {};
32
- let uniqueTrackerSize = 0;
33
-
26
+ let intervalId: any;
27
+
28
+ if (filterAdapter.refreshRate) {
29
+ intervalId = setInterval(filterAdapter.clear, filterAdapter.refreshRate);
30
+ }
31
+
34
32
  return {
35
- track(featureName: string, key: string): void {
36
- if (!filterAdapter.add(featureName, key)) {
33
+
34
+ track(key: string, featureName: string): void {
35
+ if (!filterAdapter.add(key, featureName)) {
37
36
  log.debug(`${LOG_PREFIX_UNIQUE_KEYS_TRACKER}The feature ${featureName} and key ${key} exist in the filter`);
38
37
  return;
39
38
  }
40
- if (!uniqueKeysTracker[featureName]) uniqueKeysTracker[featureName] = new _Set();
41
- const tracker = uniqueKeysTracker[featureName];
42
- if (!tracker.has(key)) {
43
- tracker.add(key);
44
- log.debug(`${LOG_PREFIX_UNIQUE_KEYS_TRACKER}Key ${key} added to feature ${featureName}`);
45
- uniqueTrackerSize++;
46
- }
47
-
48
- if (uniqueTrackerSize >= cacheSize) {
49
- log.warn(`${LOG_PREFIX_UNIQUE_KEYS_TRACKER}The UniqueKeysTracker size reached the maximum limit`);
50
- // @TODO trigger event to submitter to send mtk
51
- uniqueTrackerSize = 0;
52
- }
53
- },
54
-
55
- /**
56
- * Pop the collected data, used as payload for posting.
57
- */
58
- pop() {
59
- const data = uniqueKeysTracker;
60
- uniqueKeysTracker = {};
61
- return data;
62
- },
63
-
64
- /**
65
- * Clear the data stored on the cache.
66
- */
67
- clear() {
68
- uniqueKeysTracker = {};
39
+ uniqueKeysCache.track(key, featureName);
69
40
  },
70
41
 
71
- /**
72
- * Check if the cache is empty.
73
- */
74
- isEmpty() {
75
- return Object.keys(uniqueKeysTracker).length === 0;
42
+ stop(): void {
43
+ clearInterval(intervalId);
76
44
  }
77
45
 
78
46
  };
package/src/types.ts CHANGED
@@ -80,7 +80,6 @@ export interface ISettings {
80
80
  featuresRefreshRate: number,
81
81
  impressionsRefreshRate: number,
82
82
  impressionsQueueSize: number,
83
- uniqueKeysRefreshRate: number,
84
83
  /**
85
84
  * @deprecated
86
85
  */
@@ -50,6 +50,7 @@ export const CONSUMER_PARTIAL_ENUM = 2;
50
50
 
51
51
  export const OPTIMIZED_ENUM = 0;
52
52
  export const DEBUG_ENUM = 1;
53
+ export const NONE_ENUM = 2;
53
54
 
54
55
  export const SPLITS = 'sp';
55
56
  export const IMPRESSIONS = 'im';
@@ -24,10 +24,12 @@ THE SOFTWARE.
24
24
  **/
25
25
 
26
26
  export interface IMap<K, V> {
27
- set(key: K, value: V): this;
28
27
  clear(): void;
29
28
  delete(key: K): boolean;
29
+ forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
30
30
  get(key: K): V | undefined;
31
+ has(key: K): boolean;
32
+ set(key: K, value: V): this;
31
33
  readonly size: number;
32
34
  }
33
35
 
@@ -46,13 +48,6 @@ export class MapPoly<K, V> implements IMap<K, V>{
46
48
  this.__mapValuesData__.length = 0;
47
49
  }
48
50
 
49
- set(key: K, value: V) {
50
- let index = this.__mapKeysData__.indexOf(key);
51
- if (index === -1) index = this.__mapKeysData__.push(key) - 1;
52
- this.__mapValuesData__[index] = value;
53
- return this;
54
- }
55
-
56
51
  delete(key: K) {
57
52
  const index = this.__mapKeysData__.indexOf(key);
58
53
  if (index === -1) return false;
@@ -61,12 +56,29 @@ export class MapPoly<K, V> implements IMap<K, V>{
61
56
  return true;
62
57
  }
63
58
 
59
+ forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any) {
60
+ for (let i = 0; i < this.__mapKeysData__.length; i++) {
61
+ callbackfn.call(thisArg, this.__mapValuesData__[i], this.__mapKeysData__[i], this as any);
62
+ }
63
+ }
64
+
64
65
  get(key: K) {
65
66
  const index = this.__mapKeysData__.indexOf(key);
66
67
  if (index === -1) return;
67
68
  return this.__mapValuesData__[index];
68
69
  }
69
70
 
71
+ has(key: K): boolean {
72
+ return this.__mapKeysData__.indexOf(key) !== -1;
73
+ }
74
+
75
+ set(key: K, value: V) {
76
+ let index = this.__mapKeysData__.indexOf(key);
77
+ if (index === -1) index = this.__mapKeysData__.push(key) - 1;
78
+ this.__mapValuesData__[index] = value;
79
+ return this;
80
+ }
81
+
70
82
  get size() {
71
83
  return this.__mapKeysData__.length;
72
84
  }
@@ -0,0 +1,33 @@
1
+ //@ts-nocheck
2
+ function identityFunction(data: any): any {
3
+ return data;
4
+ }
5
+
6
+ function asyncFunction(data: any): Promise<any> {
7
+ return Promise.resolve(data);
8
+ }
9
+
10
+ const IDENTITY_METHODS: string[] = [];
11
+ const ASYNC_METHODS = ['rpush', 'hincrby'];
12
+ const PIPELINE_METHODS = ['rpush', 'hincrby'];
13
+
14
+ export class RedisMock {
15
+
16
+ private pipelineMethods: any = { exec: jest.fn(asyncFunction) }
17
+
18
+ constructor() {
19
+ IDENTITY_METHODS.forEach(method => {
20
+ this[method] = jest.fn(identityFunction);
21
+ });
22
+ ASYNC_METHODS.forEach(method => {
23
+ this[method] = jest.fn(asyncFunction);
24
+ });
25
+ PIPELINE_METHODS.forEach(method => {
26
+ this.pipelineMethods[method] = this[method];
27
+ });
28
+
29
+ this.pipeline = jest.fn(() => {return this.pipelineMethods;});
30
+ }
31
+
32
+
33
+ }
@@ -36,8 +36,6 @@ export const base = {
36
36
  telemetryRefreshRate: 3600,
37
37
  // publish evaluations each 300 sec (default value for OPTIMIZED impressions mode)
38
38
  impressionsRefreshRate: 300,
39
- // publish unique Keys each 900 sec (15 min)
40
- uniqueKeysRefreshRate: 900,
41
39
  // fetch offline changes each 15 sec
42
40
  offlineRefreshRate: 15,
43
41
  // publish events every 60 seconds after the first flush
@@ -132,13 +130,12 @@ export function settingsValidation(config: unknown, validationParams: ISettingsV
132
130
  scheduler.segmentsRefreshRate = fromSecondsToMillis(scheduler.segmentsRefreshRate);
133
131
  scheduler.offlineRefreshRate = fromSecondsToMillis(scheduler.offlineRefreshRate);
134
132
  scheduler.eventsPushRate = fromSecondsToMillis(scheduler.eventsPushRate);
135
- scheduler.uniqueKeysRefreshRate = fromSecondsToMillis(scheduler.uniqueKeysRefreshRate);
136
133
  scheduler.telemetryRefreshRate = fromSecondsToMillis(validateMinValue('telemetryRefreshRate', scheduler.telemetryRefreshRate, 60));
137
134
 
138
135
  // Default impressionsRefreshRate for DEBUG mode is 60 secs
139
136
  if (get(config, 'scheduler.impressionsRefreshRate') === undefined && withDefaults.sync.impressionsMode === DEBUG) scheduler.impressionsRefreshRate = 60;
140
137
  scheduler.impressionsRefreshRate = fromSecondsToMillis(scheduler.impressionsRefreshRate);
141
-
138
+
142
139
 
143
140
  // Log deprecation for old telemetry param
144
141
  if (scheduler.metricsRefreshRate) log.warn('`metricsRefreshRate` will be deprecated soon. For configuring telemetry rates, update `telemetryRefreshRate` value in configs');
@@ -1,5 +1,5 @@
1
1
  import { IEventsCacheBase } from '../storages/types';
2
- import { IEventsHandler, IImpressionsHandler } from '../trackers/types';
2
+ import { IEventsHandler, IImpressionsHandler, ITelemetryTracker } from '../trackers/types';
3
3
  import { ISettings, SplitIO } from '../types';
4
4
  export interface IIntegration {
5
5
  queue(data: SplitIO.IntegrationData): void;
@@ -10,6 +10,7 @@ export interface IIntegrationFactoryParams {
10
10
  events: IEventsCacheBase;
11
11
  };
12
12
  settings: ISettings;
13
+ telemetryTracker: ITelemetryTracker;
13
14
  }
14
15
  export declare type IntegrationFactory = {
15
16
  readonly type: string;
@@ -24,8 +24,8 @@ export declare type IPostUniqueKeysBulkCs = (body: string, headers?: Record<stri
24
24
  export declare type IPostUniqueKeysBulkSs = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
25
25
  export declare type IPostTestImpressionsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
26
26
  export declare type IPostTestImpressionsCount = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
27
- export declare type IPostMetricsConfig = (body: string) => Promise<IResponse>;
28
- export declare type IPostMetricsUsage = (body: string) => Promise<IResponse>;
27
+ export declare type IPostMetricsConfig = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
28
+ export declare type IPostMetricsUsage = (body: string, headers?: Record<string, string>) => Promise<IResponse>;
29
29
  export interface ISplitApi {
30
30
  getSdkAPIHealthCheck: IHealthCheckAPI;
31
31
  getEventsAPIHealthCheck: IHealthCheckAPI;