@splitsoftware/splitio-commons 1.16.1-rc.1 → 1.16.1-rc.11

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 (144) hide show
  1. package/CHANGES.txt +4 -0
  2. package/cjs/logger/constants.js +5 -4
  3. package/cjs/logger/messages/info.js +2 -1
  4. package/cjs/logger/messages/warn.js +1 -1
  5. package/cjs/readiness/readinessManager.js +7 -12
  6. package/cjs/services/splitApi.js +5 -9
  7. package/cjs/storages/AbstractSegmentsCacheSync.js +41 -12
  8. package/cjs/storages/AbstractSplitsCacheAsync.js +2 -2
  9. package/cjs/storages/AbstractSplitsCacheSync.js +7 -5
  10. package/cjs/storages/KeyBuilder.js +0 -3
  11. package/cjs/storages/KeyBuilderCS.js +6 -0
  12. package/cjs/storages/dataLoader.js +1 -1
  13. package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -52
  14. package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +4 -16
  15. package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +9 -40
  16. package/cjs/storages/inMemory/SplitsCacheInMemory.js +6 -15
  17. package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
  18. package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  19. package/cjs/sync/polling/pollingManagerCS.js +33 -51
  20. package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  21. package/cjs/sync/polling/updaters/mySegmentsUpdater.js +14 -20
  22. package/cjs/sync/streaming/AuthClient/index.js +1 -1
  23. package/cjs/sync/streaming/SSEHandler/index.js +3 -6
  24. package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -9
  25. package/cjs/sync/streaming/constants.js +3 -4
  26. package/cjs/sync/streaming/parseUtils.js +14 -9
  27. package/cjs/sync/streaming/pushManager.js +29 -55
  28. package/cjs/sync/submitters/telemetrySubmitter.js +0 -2
  29. package/cjs/sync/syncManagerOnline.js +14 -24
  30. package/cjs/utils/constants/index.js +4 -5
  31. package/cjs/utils/settingsValidation/index.js +1 -5
  32. package/esm/logger/constants.js +2 -1
  33. package/esm/logger/messages/info.js +2 -1
  34. package/esm/logger/messages/warn.js +1 -1
  35. package/esm/readiness/readinessManager.js +7 -12
  36. package/esm/services/splitApi.js +6 -10
  37. package/esm/storages/AbstractSegmentsCacheSync.js +41 -12
  38. package/esm/storages/AbstractSplitsCacheAsync.js +2 -2
  39. package/esm/storages/AbstractSplitsCacheSync.js +5 -3
  40. package/esm/storages/KeyBuilder.js +0 -3
  41. package/esm/storages/KeyBuilderCS.js +6 -0
  42. package/esm/storages/dataLoader.js +1 -1
  43. package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +29 -52
  44. package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -17
  45. package/esm/storages/inMemory/MySegmentsCacheInMemory.js +9 -40
  46. package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -16
  47. package/esm/sync/polling/fetchers/mySegmentsFetcher.js +4 -11
  48. package/esm/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
  49. package/esm/sync/polling/pollingManagerCS.js +34 -52
  50. package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +2 -2
  51. package/esm/sync/polling/updaters/mySegmentsUpdater.js +12 -18
  52. package/esm/sync/streaming/AuthClient/index.js +1 -1
  53. package/esm/sync/streaming/SSEHandler/index.js +4 -7
  54. package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +15 -9
  55. package/esm/sync/streaming/constants.js +2 -3
  56. package/esm/sync/streaming/parseUtils.js +12 -8
  57. package/esm/sync/streaming/pushManager.js +32 -57
  58. package/esm/sync/submitters/telemetrySubmitter.js +0 -2
  59. package/esm/sync/syncManagerOnline.js +15 -25
  60. package/esm/utils/constants/index.js +2 -3
  61. package/esm/utils/settingsValidation/index.js +1 -5
  62. package/package.json +1 -1
  63. package/src/dtos/types.ts +7 -8
  64. package/src/logger/constants.ts +2 -1
  65. package/src/logger/messages/info.ts +2 -1
  66. package/src/logger/messages/warn.ts +1 -1
  67. package/src/readiness/readinessManager.ts +7 -9
  68. package/src/readiness/types.ts +0 -1
  69. package/src/services/splitApi.ts +7 -12
  70. package/src/services/splitHttpClient.ts +1 -1
  71. package/src/services/types.ts +2 -3
  72. package/src/storages/AbstractSegmentsCacheSync.ts +53 -12
  73. package/src/storages/AbstractSplitsCacheAsync.ts +2 -2
  74. package/src/storages/AbstractSplitsCacheSync.ts +7 -5
  75. package/src/storages/KeyBuilder.ts +0 -3
  76. package/src/storages/KeyBuilderCS.ts +9 -0
  77. package/src/storages/dataLoader.ts +1 -1
  78. package/src/storages/inLocalStorage/MySegmentsCacheInLocal.ts +26 -56
  79. package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +5 -20
  80. package/src/storages/inMemory/MySegmentsCacheInMemory.ts +10 -44
  81. package/src/storages/inMemory/SplitsCacheInMemory.ts +7 -13
  82. package/src/storages/types.ts +10 -8
  83. package/src/sync/polling/fetchers/mySegmentsFetcher.ts +7 -14
  84. package/src/sync/polling/fetchers/segmentChangesFetcher.ts +1 -1
  85. package/src/sync/polling/fetchers/types.ts +2 -2
  86. package/src/sync/polling/pollingManagerCS.ts +29 -61
  87. package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +12 -13
  88. package/src/sync/polling/types.ts +8 -8
  89. package/src/sync/polling/updaters/mySegmentsUpdater.ts +17 -18
  90. package/src/sync/streaming/AuthClient/index.ts +1 -1
  91. package/src/sync/streaming/SSEClient/index.ts +4 -6
  92. package/src/sync/streaming/SSEHandler/index.ts +5 -9
  93. package/src/sync/streaming/SSEHandler/types.ts +13 -25
  94. package/src/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.ts +17 -12
  95. package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
  96. package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +1 -1
  97. package/src/sync/streaming/UpdateWorkers/types.ts +2 -2
  98. package/src/sync/streaming/constants.ts +2 -3
  99. package/src/sync/streaming/parseUtils.ts +19 -11
  100. package/src/sync/streaming/pushManager.ts +38 -68
  101. package/src/sync/streaming/types.ts +11 -13
  102. package/src/sync/submitters/telemetrySubmitter.ts +0 -2
  103. package/src/sync/submitters/types.ts +3 -6
  104. package/src/sync/syncManagerOnline.ts +11 -19
  105. package/src/types.ts +1 -26
  106. package/src/utils/constants/index.ts +2 -3
  107. package/src/utils/settingsValidation/index.ts +1 -5
  108. package/types/dtos/types.d.ts +7 -8
  109. package/types/logger/constants.d.ts +2 -1
  110. package/types/readiness/types.d.ts +0 -1
  111. package/types/services/decorateHeaders.d.ts +2 -0
  112. package/types/services/splitApi.d.ts +1 -1
  113. package/types/services/splitHttpClient.d.ts +1 -1
  114. package/types/services/types.d.ts +2 -3
  115. package/types/storages/AbstractSegmentsCacheSync.d.ts +9 -11
  116. package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
  117. package/types/storages/AbstractSplitsCacheSync.d.ts +4 -4
  118. package/types/storages/KeyBuilder.d.ts +0 -1
  119. package/types/storages/KeyBuilderCS.d.ts +2 -0
  120. package/types/storages/inLocalStorage/MySegmentsCacheInLocal.d.ts +2 -12
  121. package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +1 -1
  122. package/types/storages/inMemory/MySegmentsCacheInMemory.d.ts +3 -9
  123. package/types/storages/inMemory/SplitsCacheInMemory.d.ts +1 -2
  124. package/types/storages/types.d.ts +8 -7
  125. package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +2 -2
  126. package/types/sync/polling/fetchers/types.d.ts +2 -2
  127. package/types/sync/polling/syncTasks/mySegmentsSyncTask.d.ts +4 -3
  128. package/types/sync/polling/types.d.ts +8 -12
  129. package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +3 -2
  130. package/types/sync/streaming/SSEHandler/types.d.ts +13 -22
  131. package/types/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.d.ts +2 -3
  132. package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +2 -1
  133. package/types/sync/streaming/UpdateWorkers/SplitsUpdateWorker.d.ts +3 -2
  134. package/types/sync/streaming/UpdateWorkers/types.d.ts +2 -2
  135. package/types/sync/streaming/constants.d.ts +2 -3
  136. package/types/sync/streaming/parseUtils.d.ts +4 -5
  137. package/types/sync/streaming/pushManager.d.ts +0 -2
  138. package/types/sync/streaming/pushManagerCS_Spec1_3.d.ts +9 -0
  139. package/types/sync/streaming/pushManager_Spec1_3.d.ts +9 -0
  140. package/types/sync/streaming/types.d.ts +9 -10
  141. package/types/sync/submitters/types.d.ts +3 -6
  142. package/types/types.d.ts +0 -25
  143. package/types/utils/constants/index.d.ts +2 -3
  144. package/types/utils/settingsValidation/index.d.ts +0 -2
@@ -1,31 +1,24 @@
1
- import { IFetchMySegments, IResponse } from '../../../services/types';
2
- import { IMySegmentsResponse, IMyLargeSegmentsResponse } from '../../../dtos/types';
1
+ import { IFetchMemberships, IResponse } from '../../../services/types';
2
+ import { IMembershipsResponse } from '../../../dtos/types';
3
3
  import { IMySegmentsFetcher } from './types';
4
4
 
5
5
  /**
6
6
  * Factory of MySegments fetcher.
7
7
  * MySegments fetcher is a wrapper around `mySegments` API service that parses the response and handle errors.
8
8
  */
9
- export function mySegmentsFetcherFactory(fetchMySegments: IFetchMySegments): IMySegmentsFetcher {
9
+ export function mySegmentsFetcherFactory(fetchMemberships: IFetchMemberships): IMySegmentsFetcher {
10
10
 
11
11
  return function mySegmentsFetcher(
12
12
  userMatchingKey: string,
13
13
  noCache?: boolean,
14
- // Optional decorator for `fetchMySegments` promise, such as timeout or time tracker
14
+ // Optional decorator for `fetchMemberships` promise, such as timeout or time tracker
15
15
  decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
16
- ): Promise<string[]> {
16
+ ): Promise<IMembershipsResponse> {
17
17
 
18
- let mySegmentsPromise = fetchMySegments(userMatchingKey, noCache);
18
+ let mySegmentsPromise = fetchMemberships(userMatchingKey, noCache);
19
19
  if (decorator) mySegmentsPromise = decorator(mySegmentsPromise);
20
20
 
21
- // Extract segment names
22
- return mySegmentsPromise
23
- .then(resp => resp.json())
24
- .then((json: IMySegmentsResponse | IMyLargeSegmentsResponse) => {
25
- return (json as IMySegmentsResponse).mySegments ?
26
- (json as IMySegmentsResponse).mySegments.map((segment) => segment.name) :
27
- (json as IMyLargeSegmentsResponse).myLargeSegments;
28
- });
21
+ return mySegmentsPromise.then(resp => resp.json());
29
22
  };
30
23
 
31
24
  }
@@ -28,7 +28,7 @@ export function segmentChangesFetcherFactory(fetchSegmentChanges: IFetchSegmentC
28
28
  segmentName: string,
29
29
  noCache?: boolean,
30
30
  till?: number,
31
- // Optional decorator for `fetchMySegments` promise, such as timeout or time tracker
31
+ // Optional decorator for `fetchSegmentChanges` promise, such as timeout or time tracker
32
32
  decorator?: (promise: Promise<ISegmentChangesResponse[]>) => Promise<ISegmentChangesResponse[]>
33
33
  ): Promise<ISegmentChangesResponse[]> {
34
34
 
@@ -1,4 +1,4 @@
1
- import { ISplitChangesResponse, ISegmentChangesResponse } from '../../../dtos/types';
1
+ import { ISplitChangesResponse, ISegmentChangesResponse, IMembershipsResponse } from '../../../dtos/types';
2
2
  import { IResponse } from '../../../services/types';
3
3
 
4
4
  export type ISplitChangesFetcher = (
@@ -20,4 +20,4 @@ export type IMySegmentsFetcher = (
20
20
  userMatchingKey: string,
21
21
  noCache?: boolean,
22
22
  decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
23
- ) => Promise<string[]>
23
+ ) => Promise<IMembershipsResponse>
@@ -6,9 +6,8 @@ import { mySegmentsSyncTaskFactory } from './syncTasks/mySegmentsSyncTask';
6
6
  import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask';
7
7
  import { getMatching } from '../../utils/key';
8
8
  import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../readiness/constants';
9
- import { POLLING_START, POLLING_STOP } from '../../logger/constants';
9
+ import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger/constants';
10
10
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
11
- import { IN_LARGE_SEGMENT, IN_SEGMENT } from '../../utils/constants';
12
11
 
13
12
  /**
14
13
  * Expose start / stop mechanism for polling data from services.
@@ -23,92 +22,62 @@ export function pollingManagerCSFactory(
23
22
 
24
23
  const splitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings, true);
25
24
 
26
- // Map of matching keys to their corresponding MySegmentsSyncTask for segments and large segments.
27
- const mySegmentsSyncTasks: Record<string, { msSyncTask: IMySegmentsSyncTask, mlsSyncTask?: IMySegmentsSyncTask }> = {};
25
+ // Map of matching keys to their corresponding MySegmentsSyncTask.
26
+ const mySegmentsSyncTasks: Record<string, IMySegmentsSyncTask> = {};
28
27
 
29
28
  const matchingKey = getMatching(settings.core.key);
30
- const { msSyncTask, mlsSyncTask } = add(matchingKey, readiness, storage);
29
+ const mySegmentsSyncTask = add(matchingKey, readiness, storage);
31
30
 
32
31
  function startMySegmentsSyncTasks() {
33
- const splitsHaveSegments = storage.splits.usesMatcher(IN_SEGMENT);
34
- const splitsHaveLargeSegments = storage.splits.usesMatcher(IN_LARGE_SEGMENT);
35
-
36
- forOwn(mySegmentsSyncTasks, ({ msSyncTask, mlsSyncTask }) => {
37
- if (splitsHaveSegments) msSyncTask.start();
38
- else msSyncTask.stop(); // smart pausing
39
-
40
- if (mlsSyncTask) {
41
- if (splitsHaveLargeSegments) mlsSyncTask.start();
42
- else mlsSyncTask.stop(); // smart pausing
43
- }
32
+ forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
33
+ mySegmentsSyncTask.start();
44
34
  });
45
35
  }
46
36
 
47
37
  function stopMySegmentsSyncTasks() {
48
- forOwn(mySegmentsSyncTasks, ({ msSyncTask, mlsSyncTask }) => {
49
- msSyncTask.stop();
50
- mlsSyncTask && mlsSyncTask.stop();
38
+ forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
39
+ if (mySegmentsSyncTask.isRunning()) mySegmentsSyncTask.stop();
51
40
  });
52
41
  }
53
42
 
43
+ // smart pausing
54
44
  readiness.splits.on(SDK_SPLITS_ARRIVED, () => {
55
- if (splitsSyncTask.isRunning()) startMySegmentsSyncTasks();
45
+ if (!splitsSyncTask.isRunning()) return; // noop if not doing polling
46
+ const splitsHaveSegments = storage.splits.usesSegments();
47
+ if (splitsHaveSegments !== mySegmentsSyncTask.isRunning()) {
48
+ log.info(POLLING_SMART_PAUSING, [splitsHaveSegments ? 'ON' : 'OFF']);
49
+ if (splitsHaveSegments) {
50
+ startMySegmentsSyncTasks();
51
+ } else {
52
+ stopMySegmentsSyncTasks();
53
+ }
54
+ }
56
55
  });
57
56
 
58
57
  function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync) {
59
- const msSyncTask = mySegmentsSyncTaskFactory(
60
- splitApi.fetchMySegments,
61
- storage.segments,
62
- () => { if (storage.splits.usesMatcher(IN_SEGMENT)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED); },
63
- settings,
64
- matchingKey,
65
- settings.scheduler.segmentsRefreshRate,
66
- 'mySegmentsUpdater'
67
- );
68
-
69
- let mlsSyncTask;
70
- if (settings.sync.largeSegmentsEnabled) {
71
- mlsSyncTask = mySegmentsSyncTaskFactory(
72
- splitApi.fetchMyLargeSegments,
73
- storage.largeSegments!,
74
- () => { if (readiness.largeSegments && storage.splits.usesMatcher(IN_LARGE_SEGMENT)) readiness.largeSegments.emit(SDK_SEGMENTS_ARRIVED); },
75
- settings,
76
- matchingKey,
77
- settings.scheduler.largeSegmentsRefreshRate,
78
- 'myLargeSegmentsUpdater'
79
- );
80
- }
58
+ const mySegmentsSyncTask = mySegmentsSyncTaskFactory(splitApi.fetchMemberships, storage, readiness, settings, matchingKey);
81
59
 
82
60
  // smart ready
83
61
  function smartReady() {
84
- if (!readiness.isReady()) {
85
- if (readiness.largeSegments && !storage.splits.usesMatcher(IN_LARGE_SEGMENT)) readiness.largeSegments.emit(SDK_SEGMENTS_ARRIVED);
86
- if (!storage.splits.usesMatcher(IN_SEGMENT)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
87
- }
62
+ if (!readiness.isReady() && !storage.splits.usesSegments()) readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
88
63
  }
64
+ if (!storage.splits.usesSegments()) setTimeout(smartReady, 0);
65
+ else readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
89
66
 
90
- if (storage.splits.usesMatcher(IN_SEGMENT) && storage.splits.usesMatcher(IN_LARGE_SEGMENT)) readiness.splits.once(SDK_SPLITS_ARRIVED, smartReady);
91
- else setTimeout(smartReady, 0);
92
-
93
- mySegmentsSyncTasks[matchingKey] = { msSyncTask: msSyncTask, mlsSyncTask: mlsSyncTask };
94
-
95
- return {
96
- msSyncTask,
97
- mlsSyncTask
98
- };
67
+ mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask;
68
+ return mySegmentsSyncTask;
99
69
  }
100
70
 
101
71
  return {
102
72
  splitsSyncTask,
103
- segmentsSyncTask: msSyncTask,
104
- largeSegmentsSyncTask: mlsSyncTask,
73
+ segmentsSyncTask: mySegmentsSyncTask,
105
74
 
106
75
  // Start periodic fetching (polling)
107
76
  start() {
108
77
  log.info(POLLING_START);
109
78
 
110
79
  splitsSyncTask.start();
111
- startMySegmentsSyncTasks();
80
+ if (storage.splits.usesSegments()) startMySegmentsSyncTasks();
112
81
  },
113
82
 
114
83
  // Stop periodic fetching (polling)
@@ -125,9 +94,8 @@ export function pollingManagerCSFactory(
125
94
  // fetch splits and segments
126
95
  syncAll() {
127
96
  const promises = [splitsSyncTask.execute()];
128
- forOwn(mySegmentsSyncTasks, function ({ msSyncTask, mlsSyncTask }) {
129
- promises.push(msSyncTask.execute());
130
- mlsSyncTask && promises.push(mlsSyncTask.execute());
97
+ forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
98
+ promises.push(mySegmentsSyncTask.execute());
131
99
  });
132
100
  return Promise.all(promises);
133
101
  },
@@ -1,7 +1,8 @@
1
- import { ISegmentsCacheSync } from '../../../storages/types';
1
+ import { IStorageSync } from '../../../storages/types';
2
+ import { IReadinessManager } from '../../../readiness/types';
2
3
  import { syncTaskFactory } from '../../syncTask';
3
4
  import { IMySegmentsSyncTask } from '../types';
4
- import { IFetchMySegments } from '../../../services/types';
5
+ import { IFetchMemberships } from '../../../services/types';
5
6
  import { mySegmentsFetcherFactory } from '../fetchers/mySegmentsFetcher';
6
7
  import { ISettings } from '../../../types';
7
8
  import { mySegmentsUpdaterFactory } from '../updaters/mySegmentsUpdater';
@@ -10,26 +11,24 @@ import { mySegmentsUpdaterFactory } from '../updaters/mySegmentsUpdater';
10
11
  * Creates a sync task that periodically executes a `mySegmentsUpdater` task
11
12
  */
12
13
  export function mySegmentsSyncTaskFactory(
13
- fetchMySegments: IFetchMySegments,
14
- mySegmentsCache: ISegmentsCacheSync,
15
- notifyUpdate: () => void,
14
+ fetchMemberships: IFetchMemberships,
15
+ storage: IStorageSync,
16
+ readiness: IReadinessManager,
16
17
  settings: ISettings,
17
- matchingKey: string,
18
- segmentsRefreshRate: number,
19
- NAME: string
18
+ matchingKey: string
20
19
  ): IMySegmentsSyncTask {
21
20
  return syncTaskFactory(
22
21
  settings.log,
23
22
  mySegmentsUpdaterFactory(
24
23
  settings.log,
25
- mySegmentsFetcherFactory(fetchMySegments),
26
- mySegmentsCache,
27
- notifyUpdate,
24
+ mySegmentsFetcherFactory(fetchMemberships),
25
+ storage,
26
+ readiness.segments,
28
27
  settings.startup.requestTimeoutBeforeReady,
29
28
  settings.startup.retriesOnFailureBeforeReady,
30
29
  matchingKey
31
30
  ),
32
- segmentsRefreshRate,
33
- NAME,
31
+ settings.scheduler.segmentsRefreshRate,
32
+ 'mySegmentsUpdater',
34
33
  );
35
34
  }
@@ -1,17 +1,18 @@
1
1
  import { ISplit } from '../../dtos/types';
2
2
  import { IReadinessManager } from '../../readiness/types';
3
3
  import { IStorageSync } from '../../storages/types';
4
+ import { MEMBERSHIP_LS_UPDATE, MEMBERSHIP_MS_UPDATE } from '../streaming/types';
4
5
  import { ITask, ISyncTask } from '../types';
5
6
 
6
7
  export interface ISplitsSyncTask extends ISyncTask<[noCache?: boolean, till?: number, splitUpdateNotification?: { payload: ISplit, changeNumber: number }], boolean> { }
7
8
 
8
9
  export interface ISegmentsSyncTask extends ISyncTask<[fetchOnlyNew?: boolean, segmentName?: string, noCache?: boolean, till?: number], boolean> { }
9
10
 
10
- export type MySegmentsData = string[] | {
11
- /* segment name */
12
- name: string,
13
- /* action: `true` for add, and `false` for delete */
14
- add: boolean
11
+ export type MySegmentsData = {
12
+ type: MEMBERSHIP_MS_UPDATE | MEMBERSHIP_LS_UPDATE
13
+ cn: number
14
+ added: string[]
15
+ removed: string[]
15
16
  }
16
17
 
17
18
  export interface IMySegmentsSyncTask extends ISyncTask<[segmentsData?: MySegmentsData, noCache?: boolean], boolean> { }
@@ -20,14 +21,13 @@ export interface IPollingManager extends ITask {
20
21
  syncAll(): Promise<any>
21
22
  splitsSyncTask: ISplitsSyncTask
22
23
  segmentsSyncTask: ISyncTask
23
- largeSegmentsSyncTask?: ISyncTask
24
24
  }
25
25
 
26
26
  /**
27
27
  * PollingManager for client-side with support for multiple clients
28
28
  */
29
29
  export interface IPollingManagerCS extends IPollingManager {
30
- add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): { msSyncTask: IMySegmentsSyncTask, mlsSyncTask?: IMySegmentsSyncTask }
30
+ add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync): IMySegmentsSyncTask
31
31
  remove(matchingKey: string): void;
32
- get(matchingKey: string): { msSyncTask: IMySegmentsSyncTask, mlsSyncTask?: IMySegmentsSyncTask } | undefined
32
+ get(matchingKey: string): IMySegmentsSyncTask | undefined
33
33
  }
@@ -1,9 +1,13 @@
1
1
  import { IMySegmentsFetcher } from '../fetchers/types';
2
- import { ISegmentsCacheSync } from '../../../storages/types';
2
+ import { IStorageSync } from '../../../storages/types';
3
+ import { ISegmentsEventEmitter } from '../../../readiness/types';
3
4
  import { timeout } from '../../../utils/promise/timeout';
5
+ import { SDK_SEGMENTS_ARRIVED } from '../../../readiness/constants';
4
6
  import { ILogger } from '../../../logger/types';
5
7
  import { SYNC_MYSEGMENTS_FETCH_RETRY } from '../../../logger/constants';
6
8
  import { MySegmentsData } from '../types';
9
+ import { IMembershipsResponse } from '../../../dtos/types';
10
+ import { MEMBERSHIP_LS_UPDATE } from '../../streaming/constants';
7
11
 
8
12
  type IMySegmentsUpdater = (segmentList?: MySegmentsData, noCache?: boolean) => Promise<boolean>
9
13
 
@@ -16,13 +20,14 @@ type IMySegmentsUpdater = (segmentList?: MySegmentsData, noCache?: boolean) => P
16
20
  export function mySegmentsUpdaterFactory(
17
21
  log: ILogger,
18
22
  mySegmentsFetcher: IMySegmentsFetcher,
19
- mySegmentsCache: ISegmentsCacheSync,
20
- notifyUpdate: () => void,
23
+ storage: IStorageSync,
24
+ segmentsEventEmitter: ISegmentsEventEmitter,
21
25
  requestTimeoutBeforeReady: number,
22
26
  retriesOnFailureBeforeReady: number,
23
27
  matchingKey: string
24
28
  ): IMySegmentsUpdater {
25
29
 
30
+ const { splits, segments, largeSegments } = storage;
26
31
  let readyOnAlreadyExistentState = true;
27
32
  let startingUp = true;
28
33
 
@@ -33,28 +38,22 @@ export function mySegmentsUpdaterFactory(
33
38
  }
34
39
 
35
40
  // @TODO if allowing pluggable storages, handle async execution
36
- function updateSegments(segmentsData: MySegmentsData) {
41
+ function updateSegments(segmentsData: IMembershipsResponse | MySegmentsData) {
37
42
 
38
43
  let shouldNotifyUpdate;
39
- if (Array.isArray(segmentsData)) {
40
- // Update the list of segment names available
41
- shouldNotifyUpdate = mySegmentsCache.resetSegments(segmentsData);
44
+ if ((segmentsData as MySegmentsData).type !== undefined) {
45
+ shouldNotifyUpdate = (segmentsData as MySegmentsData).type === MEMBERSHIP_LS_UPDATE ?
46
+ largeSegments!.resetSegments(segmentsData as MySegmentsData) :
47
+ segments.resetSegments(segmentsData as MySegmentsData);
42
48
  } else {
43
- // Add/Delete the segment
44
- const { name, add } = segmentsData;
45
- if (mySegmentsCache.isInSegment(name) !== add) {
46
- shouldNotifyUpdate = true;
47
- if (add) mySegmentsCache.addToSegment(name);
48
- else mySegmentsCache.removeFromSegment(name);
49
- } else {
50
- shouldNotifyUpdate = false;
51
- }
49
+ shouldNotifyUpdate = segments.resetSegments((segmentsData as IMembershipsResponse).ms || {});
50
+ shouldNotifyUpdate = largeSegments!.resetSegments((segmentsData as IMembershipsResponse).ls || {}) || shouldNotifyUpdate;
52
51
  }
53
52
 
54
53
  // Notify update if required
55
- if (shouldNotifyUpdate || readyOnAlreadyExistentState) {
54
+ if (splits.usesSegments() && (shouldNotifyUpdate || readyOnAlreadyExistentState)) {
56
55
  readyOnAlreadyExistentState = false;
57
- notifyUpdate();
56
+ segmentsEventEmitter.emit(SDK_SEGMENTS_ARRIVED);
58
57
  }
59
58
  }
60
59
 
@@ -14,7 +14,7 @@ export function authenticateFactory(fetchAuth: IFetchAuth): IAuthenticate {
14
14
 
15
15
  /**
16
16
  * Run authentication requests to Auth Server, and returns a promise that resolves with the decoded JTW token.
17
- * @param {string[] | undefined} userKeys set of user Keys to track MY_SEGMENTS_CHANGES. It is undefined for server-side API.
17
+ * @param {string[] | undefined} userKeys set of user Keys to track membership updates. It is undefined for server-side API.
18
18
  */
19
19
  return function authenticate(userKeys?: string[]): Promise<IAuthToken> {
20
20
  return fetchAuth(userKeys)
@@ -76,12 +76,10 @@ export class SSEClient implements ISSEClient {
76
76
  open(authToken: IAuthTokenPushEnabled) {
77
77
  this.close(); // it closes connection if previously opened
78
78
 
79
- const channelsQueryParam = Object.keys(authToken.channels).map(
80
- function (channel) {
81
- const params = CONTROL_CHANNEL_REGEX.test(channel) ? '[?occupancy=metrics.publishers]' : '';
82
- return encodeURIComponent(params + channel);
83
- }
84
- ).join(',');
79
+ const channelsQueryParam = Object.keys(authToken.channels).map((channel) => {
80
+ const params = CONTROL_CHANNEL_REGEX.test(channel) ? '[?occupancy=metrics.publishers]' : '';
81
+ return encodeURIComponent(params + channel);
82
+ }).join(',');
85
83
  const url = `${this.streamingUrl}?channels=${channelsQueryParam}&accessToken=${authToken.token}&v=${ABLY_API_VERSION}&heartbeats=true`; // same results using `&heartbeats=false`
86
84
 
87
85
  this.connection = new this.eventSource!(
@@ -1,6 +1,6 @@
1
1
  import { errorParser, messageParser } from './NotificationParser';
2
2
  import { notificationKeeperFactory } from './NotificationKeeper';
3
- import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MY_LARGE_SEGMENTS_UPDATE } from '../constants';
3
+ import { PUSH_RETRYABLE_ERROR, PUSH_NONRETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIP_MS_UPDATE, MEMBERSHIP_LS_UPDATE } from '../constants';
4
4
  import { IPushEventEmitter } from '../types';
5
5
  import { ISseEventHandler } from '../SSEClient/types';
6
6
  import { INotificationError, INotificationMessage } from './types';
@@ -74,22 +74,18 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
74
74
  const { parsedData, data, channel, timestamp } = messageWithParsedData;
75
75
  log.debug(STREAMING_NEW_MESSAGE, [data]);
76
76
 
77
- // we only handle update events if streaming is up.
78
- if (!notificationKeeper.isStreamingUp() && [OCCUPANCY, CONTROL].indexOf(parsedData.type) === -1)
79
- return;
77
+ // we only handle update events if streaming is up
78
+ if (!notificationKeeper.isStreamingUp() && [OCCUPANCY, CONTROL].indexOf(parsedData.type) === -1) return;
80
79
 
81
80
  switch (parsedData.type) {
82
81
  /* update events */
83
82
  case SPLIT_UPDATE:
84
83
  case SEGMENT_UPDATE:
85
- case MY_SEGMENTS_UPDATE_V2:
86
- case MY_LARGE_SEGMENTS_UPDATE:
84
+ case MEMBERSHIP_MS_UPDATE:
85
+ case MEMBERSHIP_LS_UPDATE:
87
86
  case SPLIT_KILL:
88
87
  pushEmitter.emit(parsedData.type, parsedData);
89
88
  break;
90
- case MY_SEGMENTS_UPDATE:
91
- pushEmitter.emit(parsedData.type, parsedData, channel);
92
- break;
93
89
 
94
90
  /* occupancy & control events, handled by NotificationManagerKeeper */
95
91
  case OCCUPANCY:
@@ -1,12 +1,5 @@
1
1
  import { ControlType } from '../constants';
2
- import { MY_SEGMENTS_UPDATE, MY_SEGMENTS_UPDATE_V2, SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MY_LARGE_SEGMENTS_UPDATE } from '../types';
3
-
4
- export interface IMySegmentsUpdateData {
5
- type: MY_SEGMENTS_UPDATE,
6
- changeNumber: number,
7
- includesPayload: boolean,
8
- segmentList?: string[]
9
- }
2
+ import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIP_LS_UPDATE, MEMBERSHIP_MS_UPDATE } from '../types';
10
3
 
11
4
  export enum Compression {
12
5
  None = 0,
@@ -26,27 +19,22 @@ export interface KeyList {
26
19
  r?: string[], // decimal hash64 of user keys
27
20
  }
28
21
 
29
- export interface IMySegmentsUpdateV2Data {
30
- type: MY_SEGMENTS_UPDATE_V2,
31
- changeNumber: number,
32
- segmentName: string,
33
- c: Compression,
34
- d: string,
35
- u: UpdateStrategy,
36
- }
37
-
38
- export interface IMyLargeSegmentsUpdateData {
39
- type: MY_LARGE_SEGMENTS_UPDATE,
40
- changeNumber: number,
41
- largeSegments: string[],
42
- c: Compression,
43
- d: string,
22
+ interface IMembershipUpdateData<T extends string> {
23
+ type: T,
24
+ cn: number,
25
+ n?: string[],
26
+ c?: Compression,
27
+ d?: string,
44
28
  u: UpdateStrategy,
45
29
  i?: number, // time interval in millis
46
- h?: number, // hash function. 0 for murmur3_32, 1 for murmur3_64
30
+ h?: number, // hash function
47
31
  s?: number, // seed for hash function
48
32
  }
49
33
 
34
+ export interface IMembershipMSUpdateData extends IMembershipUpdateData<MEMBERSHIP_MS_UPDATE> { }
35
+
36
+ export interface IMembershipLSUpdateData extends IMembershipUpdateData<MEMBERSHIP_LS_UPDATE> { }
37
+
50
38
  export interface ISegmentUpdateData {
51
39
  type: SEGMENT_UPDATE,
52
40
  changeNumber: number,
@@ -80,6 +68,6 @@ export interface IOccupancyData {
80
68
  }
81
69
  }
82
70
 
83
- export type INotificationData = IMySegmentsUpdateData | IMySegmentsUpdateV2Data | IMyLargeSegmentsUpdateData | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
71
+ export type INotificationData = IMembershipMSUpdateData | IMembershipLSUpdateData | ISegmentUpdateData | ISplitUpdateData | ISplitKillData | IControlData | IOccupancyData
84
72
  export type INotificationMessage = { parsedData: INotificationData, channel: string, timestamp: number, data: string }
85
73
  export type INotificationError = Event & { parsedData?: any, message?: string }
@@ -2,12 +2,12 @@ import { IMySegmentsSyncTask, MySegmentsData } from '../../polling/types';
2
2
  import { Backoff } from '../../../utils/Backoff';
3
3
  import { IUpdateWorker } from './types';
4
4
  import { ITelemetryTracker } from '../../../trackers/types';
5
- import { UpdatesFromSSEEnum } from '../../submitters/types';
5
+ import { MEMBERSHIPS } from '../../../utils/constants';
6
6
 
7
7
  /**
8
8
  * MySegmentsUpdateWorker factory
9
9
  */
10
- export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask, telemetryTracker: ITelemetryTracker, updateType: UpdatesFromSSEEnum): IUpdateWorker {
10
+ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask, telemetryTracker: ITelemetryTracker): IUpdateWorker<[mySegmentsData?: Pick<MySegmentsData, 'type' | 'cn'>, payload?: Pick<MySegmentsData, 'added' | 'removed'>, delay?: number]> {
11
11
 
12
12
  let maxChangeNumber = 0; // keeps the maximum changeNumber among queued events
13
13
  let currentChangeNumber = -1;
@@ -15,7 +15,7 @@ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask,
15
15
  let isHandlingEvent: boolean;
16
16
  let _segmentsData: MySegmentsData | undefined; // keeps the segmentsData (if included in notification payload) from the queued event with maximum changeNumber
17
17
  let _delay: undefined | number;
18
- let _delayTimeoutID: undefined | number;
18
+ let _delayTimeoutID: any;
19
19
  const backoff = new Backoff(__handleMySegmentsUpdateCall);
20
20
 
21
21
  function __handleMySegmentsUpdateCall() {
@@ -28,6 +28,7 @@ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask,
28
28
  const syncTask = _delay ?
29
29
  new Promise(res => {
30
30
  _delayTimeoutID = setTimeout(() => {
31
+ _delay = undefined;
31
32
  mySegmentsSyncTask.execute(_segmentsData, true).then(res);
32
33
  }, _delay);
33
34
  }) :
@@ -35,8 +36,8 @@ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask,
35
36
 
36
37
  syncTask.then((result) => {
37
38
  if (!isHandlingEvent) return; // halt if `stop` has been called
38
- if (result !== false) {// Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
39
- if (_segmentsData) telemetryTracker.trackUpdatesFromSSE(updateType);
39
+ if (result !== false) { // Unlike `Splits|SegmentsUpdateWorker`, we cannot use `mySegmentsCache.getChangeNumber` since `/mySegments` endpoint doesn't provide this value.
40
+ if (_segmentsData) telemetryTracker.trackUpdatesFromSSE(MEMBERSHIPS);
40
41
  currentChangeNumber = Math.max(currentChangeNumber, currentMaxChangeNumber); // use `currentMaxChangeNumber`, in case that `maxChangeNumber` was updated during fetch.
41
42
  }
42
43
  if (handleNewEvent) {
@@ -52,17 +53,20 @@ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask,
52
53
 
53
54
  return {
54
55
  /**
55
- * Invoked by NotificationProcessor on MY_SEGMENTS_UPDATE event
56
+ * Invoked by NotificationProcessor on MY_(LARGE)_SEGMENTS_UPDATE notifications
56
57
  *
57
- * @param {number} changeNumber change number of the MY_SEGMENTS_UPDATE notification
58
- * @param {SegmentsData | undefined} segmentsData might be undefined
58
+ * @param changeNumber change number of the notification
59
+ * @param segmentsData data for KeyList or SegmentRemoval instant updates
60
+ * @param delay optional time to wait for BoundedFetchRequest or BoundedFetchRequest updates
59
61
  */
60
- put(changeNumber: number, segmentsData?: MySegmentsData, delay?: number) {
61
- if (changeNumber <= currentChangeNumber || changeNumber <= maxChangeNumber) return;
62
+ put(mySegmentsData: Pick<MySegmentsData, 'type' | 'cn'>, payload?: Pick<MySegmentsData, 'added' | 'removed'>, delay?: number) {
63
+ const { type, cn } = mySegmentsData;
64
+ // Ignore event if it is outdated or if there is a pending fetch request (_delay is set)
65
+ if (cn <= currentChangeNumber || cn <= maxChangeNumber || _delay) return;
62
66
 
63
- maxChangeNumber = changeNumber;
67
+ maxChangeNumber = cn;
64
68
  handleNewEvent = true;
65
- _segmentsData = segmentsData;
69
+ _segmentsData = payload && { type, cn, added: payload.added, removed: payload.removed };
66
70
  _delay = delay;
67
71
 
68
72
  if (backoff.timeoutID || !isHandlingEvent) __handleMySegmentsUpdateCall();
@@ -71,6 +75,7 @@ export function MySegmentsUpdateWorker(mySegmentsSyncTask: IMySegmentsSyncTask,
71
75
 
72
76
  stop() {
73
77
  clearTimeout(_delayTimeoutID);
78
+ _delay = undefined;
74
79
  isHandlingEvent = false;
75
80
  backoff.reset();
76
81
  }
@@ -9,7 +9,7 @@ import { IUpdateWorker } from './types';
9
9
  /**
10
10
  * SegmentsUpdateWorker factory
11
11
  */
12
- export function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync): IUpdateWorker {
12
+ export function SegmentsUpdateWorker(log: ILogger, segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync): IUpdateWorker<[ISegmentUpdateData]> {
13
13
 
14
14
  // Handles retries with CDN bypass per segment name
15
15
  function SegmentUpdateWorker(segment: string) {
@@ -14,7 +14,7 @@ import { IUpdateWorker } from './types';
14
14
  /**
15
15
  * SplitsUpdateWorker factory
16
16
  */
17
- export function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker & { killSplit(event: ISplitKillData): void } {
17
+ export function SplitsUpdateWorker(log: ILogger, splitsCache: ISplitsCacheSync, splitsSyncTask: ISplitsSyncTask, splitsEventEmitter: ISplitsEventEmitter, telemetryTracker: ITelemetryTracker, segmentsSyncTask?: ISegmentsSyncTask): IUpdateWorker<[updateData: ISplitUpdateData, payload?: ISplit]> & { killSplit(event: ISplitKillData): void } {
18
18
 
19
19
  let maxChangeNumber = 0;
20
20
  let handleNewEvent = false;
@@ -1,4 +1,4 @@
1
- export interface IUpdateWorker {
1
+ export interface IUpdateWorker<T extends any[]> {
2
2
  stop(): void // clear scheduled tasks (backoff)
3
- put(...args: any[]): void // handle new update event
3
+ put(...args: T): void // handle new update event
4
4
  }
@@ -25,12 +25,11 @@ export const PUSH_SUBSYSTEM_UP = 'PUSH_SUBSYSTEM_UP';
25
25
  export const PUSH_SUBSYSTEM_DOWN = 'PUSH_SUBSYSTEM_DOWN';
26
26
 
27
27
  // Update-type push notifications, handled by NotificationProcessor
28
- export const MY_SEGMENTS_UPDATE = 'MY_SEGMENTS_UPDATE';
29
- export const MY_SEGMENTS_UPDATE_V2 = 'MY_SEGMENTS_UPDATE_V2';
28
+ export const MEMBERSHIP_MS_UPDATE = 'MEMBERSHIP_MS_UPDATE';
29
+ export const MEMBERSHIP_LS_UPDATE = 'MEMBERSHIP_LS_UPDATE';
30
30
  export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
31
31
  export const SPLIT_KILL = 'SPLIT_KILL';
32
32
  export const SPLIT_UPDATE = 'SPLIT_UPDATE';
33
- export const MY_LARGE_SEGMENTS_UPDATE = 'MY_LARGE_SEGMENTS_UPDATE';
34
33
 
35
34
  // Control-type push notifications, handled by NotificationKeeper
36
35
  export const CONTROL = 'CONTROL';