@splitsoftware/splitio-commons 0.1.1-rc.20 → 1.0.1-rc.2
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.
- package/CHANGES.txt +9 -0
- package/README.md +3 -2
- package/cjs/listeners/browser.js +3 -1
- package/cjs/logger/constants.js +4 -4
- package/cjs/logger/messages/error.js +2 -1
- package/cjs/logger/messages/warn.js +0 -1
- package/cjs/sdkClient/sdkClient.js +4 -4
- package/cjs/sdkClient/sdkClientMethodCS.js +16 -5
- package/cjs/sdkClient/sdkClientMethodCSWithTT.js +17 -6
- package/cjs/sdkFactory/index.js +6 -3
- package/cjs/storages/inMemory/InMemoryStorage.js +0 -3
- package/cjs/storages/inRedis/index.js +1 -2
- package/cjs/storages/pluggable/SplitsCachePluggable.js +1 -1
- package/cjs/storages/pluggable/inMemoryWrapper.js +19 -5
- package/cjs/storages/pluggable/index.js +38 -15
- package/cjs/storages/pluggable/wrapperAdapter.js +3 -3
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +1 -1
- package/cjs/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +0 -1
- package/cjs/sync/submitters/submitterManager.js +19 -0
- package/cjs/sync/syncManagerOnline.js +20 -26
- package/cjs/trackers/impressionObserver/utils.js +1 -1
- package/cjs/utils/constants/index.js +3 -2
- package/cjs/utils/settingsValidation/mode.js +1 -1
- package/cjs/utils/settingsValidation/storage/storageCS.js +17 -3
- package/esm/listeners/browser.js +3 -1
- package/esm/logger/constants.js +3 -3
- package/esm/logger/messages/error.js +2 -1
- package/esm/logger/messages/warn.js +0 -1
- package/esm/sdkClient/sdkClient.js +5 -5
- package/esm/sdkClient/sdkClientMethodCS.js +16 -5
- package/esm/sdkClient/sdkClientMethodCSWithTT.js +17 -6
- package/esm/sdkFactory/index.js +6 -3
- package/esm/storages/inMemory/InMemoryStorage.js +0 -3
- package/esm/storages/inRedis/index.js +1 -2
- package/esm/storages/pluggable/SplitsCachePluggable.js +1 -1
- package/esm/storages/pluggable/inMemoryWrapper.js +19 -5
- package/esm/storages/pluggable/index.js +40 -16
- package/esm/storages/pluggable/wrapperAdapter.js +3 -3
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +1 -1
- package/esm/sync/streaming/UpdateWorkers/SplitsUpdateWorker.js +0 -1
- package/esm/sync/submitters/submitterManager.js +15 -0
- package/esm/sync/syncManagerOnline.js +20 -26
- package/esm/trackers/impressionObserver/utils.js +2 -2
- package/esm/utils/constants/index.js +2 -1
- package/esm/utils/settingsValidation/mode.js +2 -2
- package/esm/utils/settingsValidation/storage/storageCS.js +19 -5
- package/package.json +1 -1
- package/src/listeners/browser.ts +3 -1
- package/src/logger/constants.ts +3 -3
- package/src/logger/messages/error.ts +2 -1
- package/src/logger/messages/warn.ts +0 -1
- package/src/sdkClient/sdkClient.ts +6 -6
- package/src/sdkClient/sdkClientMethodCS.ts +14 -4
- package/src/sdkClient/sdkClientMethodCSWithTT.ts +15 -5
- package/src/sdkFactory/index.ts +7 -3
- package/src/services/splitApi.ts +4 -1
- package/src/storages/inLocalStorage/index.ts +2 -2
- package/src/storages/inMemory/InMemoryStorage.ts +0 -3
- package/src/storages/inMemory/InMemoryStorageCS.ts +2 -2
- package/src/storages/inRedis/index.ts +1 -1
- package/src/storages/pluggable/EventsCachePluggable.ts +3 -3
- package/src/storages/pluggable/ImpressionsCachePluggable.ts +3 -3
- package/src/storages/pluggable/SegmentsCachePluggable.ts +3 -3
- package/src/storages/pluggable/SplitsCachePluggable.ts +4 -4
- package/src/storages/pluggable/inMemoryWrapper.ts +20 -6
- package/src/storages/pluggable/index.ts +46 -16
- package/src/storages/pluggable/wrapperAdapter.ts +5 -5
- package/src/storages/types.ts +24 -24
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +1 -1
- package/src/sync/streaming/UpdateWorkers/SplitsUpdateWorker.ts +0 -1
- package/src/sync/submitters/submitterManager.ts +22 -0
- package/src/sync/syncManagerOnline.ts +26 -32
- package/src/sync/types.ts +1 -1
- package/src/trackers/impressionObserver/ImpressionObserver.ts +1 -1
- package/src/trackers/impressionObserver/utils.ts +2 -2
- package/src/types.ts +5 -5
- package/src/utils/constants/index.ts +6 -4
- package/src/utils/settingsValidation/mode.ts +2 -2
- package/src/utils/settingsValidation/storage/storageCS.ts +21 -8
- package/types/logger/constants.d.ts +3 -3
- package/types/services/splitApi.d.ts +1 -1
- package/types/storages/inMemory/InMemoryStorageCS.d.ts +2 -2
- package/types/storages/pluggable/EventsCachePluggable.d.ts +2 -2
- package/types/storages/pluggable/ImpressionsCachePluggable.d.ts +2 -2
- package/types/storages/pluggable/SegmentsCachePluggable.d.ts +5 -5
- package/types/storages/pluggable/SplitsCachePluggable.d.ts +3 -3
- package/types/storages/pluggable/inMemoryWrapper.d.ts +6 -3
- package/types/storages/pluggable/index.d.ts +2 -2
- package/types/storages/pluggable/wrapperAdapter.d.ts +4 -4
- package/types/storages/types.d.ts +21 -22
- package/types/sync/submitters/submitterManager.d.ts +4 -0
- package/types/sync/syncManagerOnline.d.ts +1 -1
- package/types/sync/types.d.ts +1 -1
- package/types/trackers/impressionObserver/ImpressionObserver.d.ts +1 -1
- package/types/types.d.ts +5 -5
- package/types/utils/constants/index.d.ts +6 -4
- package/types/utils/settingsValidation/storage/storageCS.d.ts +6 -4
package/src/listeners/browser.ts
CHANGED
|
@@ -63,6 +63,8 @@ export default class BrowserSignalListener implements ISignalListener {
|
|
|
63
63
|
* using beacon API if possible, or falling back to regular post transport.
|
|
64
64
|
*/
|
|
65
65
|
flushData() {
|
|
66
|
+
if (!this.syncManager) return; // In consumer mode there is not sync manager and data to flush
|
|
67
|
+
|
|
66
68
|
const eventsUrl = this.settings.urls.events;
|
|
67
69
|
const extraMetadata = {
|
|
68
70
|
// sim stands for Sync/Split Impressions Mode
|
|
@@ -74,7 +76,7 @@ export default class BrowserSignalListener implements ISignalListener {
|
|
|
74
76
|
if (this.storage.impressionCounts) this._flushData(eventsUrl + '/testImpressions/count/beacon', this.storage.impressionCounts, this.serviceApi.postTestImpressionsCount, fromImpressionCountsCollector);
|
|
75
77
|
|
|
76
78
|
// Close streaming connection
|
|
77
|
-
if (this.syncManager
|
|
79
|
+
if (this.syncManager.pushManager) this.syncManager.pushManager.stop();
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
private _flushData<TState>(url: string, cache: IRecorderCacheProducerSync<TState>, postService: (body: string) => Promise<IResponse>, fromCacheToPayload?: (cacheData: TState) => any, extraMetadata?: {}) {
|
package/src/logger/constants.ts
CHANGED
|
@@ -91,9 +91,8 @@ export const WARN_INTEGRATION_INVALID = 218;
|
|
|
91
91
|
export const WARN_SPLITS_FILTER_IGNORED = 219;
|
|
92
92
|
export const WARN_SPLITS_FILTER_INVALID = 220;
|
|
93
93
|
export const WARN_SPLITS_FILTER_EMPTY = 221;
|
|
94
|
-
export const
|
|
95
|
-
export const
|
|
96
|
-
export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 224;
|
|
94
|
+
export const WARN_API_KEY = 222;
|
|
95
|
+
export const STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2 = 223;
|
|
97
96
|
|
|
98
97
|
export const ERROR_ENGINE_COMBINER_IFELSEIF = 300;
|
|
99
98
|
export const ERROR_LOGLEVEL_INVALID = 301;
|
|
@@ -119,6 +118,7 @@ export const ERROR_EMPTY_ARRAY = 320;
|
|
|
119
118
|
export const ERROR_INVALID_IMPRESSIONS_MODE = 321;
|
|
120
119
|
export const ERROR_HTTP = 322;
|
|
121
120
|
export const ERROR_LOCALHOST_MODULE_REQUIRED = 323;
|
|
121
|
+
export const ERROR_STORAGE_INVALID = 324;
|
|
122
122
|
|
|
123
123
|
// Log prefixes (a.k.a. tags or categories)
|
|
124
124
|
export const LOG_PREFIX_SETTINGS = 'settings';
|
|
@@ -30,5 +30,6 @@ export const codesError: [number, string][] = [
|
|
|
30
30
|
[c.ERROR_EMPTY_ARRAY, '%s: %s must be a non-empty array.'],
|
|
31
31
|
// initialization / settings validation
|
|
32
32
|
[c.ERROR_INVALID_IMPRESSIONS_MODE, c.LOG_PREFIX_SETTINGS + ': you passed an invalid "impressionsMode". It should be one of the following values: %s. Defaulting to "%s" mode.'],
|
|
33
|
-
[c.ERROR_LOCALHOST_MODULE_REQUIRED, c.LOG_PREFIX_SETTINGS + ': an invalid value was received for "sync.localhostMode" config. A valid entity should be provided for localhost mode.']
|
|
33
|
+
[c.ERROR_LOCALHOST_MODULE_REQUIRED, c.LOG_PREFIX_SETTINGS + ': an invalid value was received for "sync.localhostMode" config. A valid entity should be provided for localhost mode.'],
|
|
34
|
+
[c.ERROR_STORAGE_INVALID, c.LOG_PREFIX_SETTINGS+': The provided storage is invalid.%s Fallbacking into default MEMORY storage'],
|
|
34
35
|
];
|
|
@@ -29,7 +29,6 @@ export const codesWarn: [number, string][] = codesError.concat([
|
|
|
29
29
|
[c.WARN_SPLITS_FILTER_IGNORED, c.LOG_PREFIX_SETTINGS+': split filters have been configured but will have no effect if mode is not "%s", since synchronization is being deferred to an external tool.'],
|
|
30
30
|
[c.WARN_SPLITS_FILTER_INVALID, c.LOG_PREFIX_SETTINGS+': split filter at position %s is invalid. It must be an object with a valid filter type ("byName" or "byPrefix") and a list of "values".'],
|
|
31
31
|
[c.WARN_SPLITS_FILTER_EMPTY, c.LOG_PREFIX_SETTINGS+': splitFilters configuration must be a non-empty array of filter objects.'],
|
|
32
|
-
[c.WARN_STORAGE_INVALID, c.LOG_PREFIX_SETTINGS+': The provided storage is invalid. Fallbacking into default MEMORY storage'],
|
|
33
32
|
[c.WARN_API_KEY, c.LOG_PREFIX_SETTINGS+': You already have %s. We recommend keeping only one instance of the factory at all times (Singleton pattern) and reusing it throughout your application'],
|
|
34
33
|
|
|
35
34
|
[c.STREAMING_PARSING_MY_SEGMENTS_UPDATE_V2, c.LOG_PREFIX_SYNC_STREAMING + 'Fetching MySegments due to an error processing %s notification: %s'],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import objectAssign from 'object-assign';
|
|
2
2
|
import { IStatusInterface, SplitIO } from '../types';
|
|
3
|
-
import { CONSUMER_MODE } from '../utils/constants';
|
|
3
|
+
import { CONSUMER_MODE, CONSUMER_PARTIAL_MODE } from '../utils/constants';
|
|
4
4
|
import { releaseApiKey } from '../utils/inputValidation/apiKey';
|
|
5
5
|
import clientFactory from './client';
|
|
6
6
|
import clientInputValidationDecorator from './clientInputValidation';
|
|
@@ -21,8 +21,8 @@ export function sdkClientFactory(params: ISdkClientFactoryParams): SplitIO.IClie
|
|
|
21
21
|
settings.log,
|
|
22
22
|
clientFactory(params),
|
|
23
23
|
sdkReadinessManager.readinessManager,
|
|
24
|
-
//
|
|
25
|
-
settings.mode ===
|
|
24
|
+
// storage is async if and only if mode is consumer or partial consumer
|
|
25
|
+
[CONSUMER_MODE, CONSUMER_PARTIAL_MODE].indexOf(settings.mode) === -1 ? true : false,
|
|
26
26
|
),
|
|
27
27
|
|
|
28
28
|
// Sdk destroy
|
|
@@ -37,11 +37,11 @@ export function sdkClientFactory(params: ISdkClientFactoryParams): SplitIO.IClie
|
|
|
37
37
|
sdkReadinessManager.readinessManager.destroy();
|
|
38
38
|
signalListener && signalListener.stop();
|
|
39
39
|
|
|
40
|
-
// Cleanup storage
|
|
41
|
-
storage.destroy();
|
|
42
|
-
|
|
43
40
|
// Release the API Key if it is the main client
|
|
44
41
|
if (!sharedClient) releaseApiKey(settings.core.authorizationKey);
|
|
42
|
+
|
|
43
|
+
// Cleanup storage
|
|
44
|
+
return storage.destroy();
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -4,10 +4,10 @@ import { SplitIO } from '../types';
|
|
|
4
4
|
import { validateKey } from '../utils/inputValidation/key';
|
|
5
5
|
import { getMatching, keyParser } from '../utils/key';
|
|
6
6
|
import { sdkClientFactory } from './sdkClient';
|
|
7
|
-
import { IStorageSyncCS } from '../storages/types';
|
|
8
7
|
import { ISyncManagerCS } from '../sync/types';
|
|
9
8
|
import objectAssign from 'object-assign';
|
|
10
9
|
import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants';
|
|
10
|
+
import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
|
|
11
11
|
|
|
12
12
|
function buildInstanceId(key: SplitIO.SplitKey) {
|
|
13
13
|
// @ts-ignore
|
|
@@ -58,15 +58,25 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
|
|
|
58
58
|
const matchingKey = getMatching(validKey);
|
|
59
59
|
|
|
60
60
|
const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout);
|
|
61
|
-
const sharedStorage =
|
|
62
|
-
|
|
61
|
+
const sharedStorage = storage.shared && storage.shared(matchingKey, (err) => {
|
|
62
|
+
if (err) return;
|
|
63
|
+
// Emit SDK_READY in consumer mode for shared clients
|
|
64
|
+
sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// 3 possibilities:
|
|
68
|
+
// - Standalone mode: both syncManager and sharedSyncManager are defined
|
|
69
|
+
// - Consumer mode: both syncManager and sharedSyncManager are undefined
|
|
70
|
+
// - Consumer partial mode: syncManager is defined (only for submitters) but sharedSyncManager is undefined
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage);
|
|
63
73
|
|
|
64
74
|
// As shared clients reuse all the storage information, we don't need to check here if we
|
|
65
75
|
// will use offline or online mode. We should stick with the original decision.
|
|
66
76
|
clientInstances[instanceId] = clientCSDecorator(
|
|
67
77
|
sdkClientFactory(objectAssign({}, params, {
|
|
68
78
|
sdkReadinessManager: sharedSdkReadiness,
|
|
69
|
-
storage: sharedStorage,
|
|
79
|
+
storage: sharedStorage || storage,
|
|
70
80
|
syncManager: sharedSyncManager,
|
|
71
81
|
signalListener: undefined, // only the main client "destroy" method stops the signal listener
|
|
72
82
|
sharedClient: true
|
|
@@ -5,10 +5,10 @@ import { validateKey } from '../utils/inputValidation/key';
|
|
|
5
5
|
import { validateTrafficType } from '../utils/inputValidation/trafficType';
|
|
6
6
|
import { getMatching, keyParser } from '../utils/key';
|
|
7
7
|
import { sdkClientFactory } from './sdkClient';
|
|
8
|
-
import { IStorageSyncCS } from '../storages/types';
|
|
9
8
|
import { ISyncManagerCS } from '../sync/types';
|
|
10
9
|
import objectAssign from 'object-assign';
|
|
11
10
|
import { RETRIEVE_CLIENT_DEFAULT, NEW_SHARED_CLIENT, RETRIEVE_CLIENT_EXISTING } from '../logger/constants';
|
|
11
|
+
import { SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
|
|
12
12
|
|
|
13
13
|
function buildInstanceId(key: SplitIO.SplitKey, trafficType?: string) {
|
|
14
14
|
// @ts-ignore
|
|
@@ -72,15 +72,25 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
|
|
|
72
72
|
const matchingKey = getMatching(validKey);
|
|
73
73
|
|
|
74
74
|
const sharedSdkReadiness = sdkReadinessManager.shared(readyTimeout);
|
|
75
|
-
const sharedStorage =
|
|
76
|
-
|
|
75
|
+
const sharedStorage = storage.shared && storage.shared(matchingKey, (err) => {
|
|
76
|
+
if (err) return;
|
|
77
|
+
// Emit SDK_READY in consumer mode for shared clients
|
|
78
|
+
sharedSdkReadiness.readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// 3 possibilities:
|
|
82
|
+
// - Standalone mode: both syncManager and sharedSyncManager are defined
|
|
83
|
+
// - Consumer mode: both syncManager and sharedSyncManager are undefined
|
|
84
|
+
// - Consumer partial mode: syncManager is defined (only for submitters) but sharedSyncManager is undefined
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
const sharedSyncManager = syncManager && sharedStorage && (syncManager as ISyncManagerCS).shared(matchingKey, sharedSdkReadiness.readinessManager, sharedStorage);
|
|
77
87
|
|
|
78
88
|
// As shared clients reuse all the storage information, we don't need to check here if we
|
|
79
89
|
// will use offline or online mode. We should stick with the original decision.
|
|
80
90
|
clientInstances[instanceId] = clientCSDecorator(
|
|
81
91
|
sdkClientFactory(objectAssign({}, params, {
|
|
82
92
|
sdkReadinessManager: sharedSdkReadiness,
|
|
83
|
-
storage: sharedStorage,
|
|
93
|
+
storage: sharedStorage || storage,
|
|
84
94
|
syncManager: sharedSyncManager,
|
|
85
95
|
signalListener: undefined, // only the main client "destroy" method stops the signal listener
|
|
86
96
|
sharedClient: true
|
|
@@ -89,7 +99,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
|
|
|
89
99
|
validTrafficType
|
|
90
100
|
);
|
|
91
101
|
|
|
92
|
-
sharedSyncManager.start();
|
|
102
|
+
sharedSyncManager && sharedSyncManager.start();
|
|
93
103
|
|
|
94
104
|
log.info(NEW_SHARED_CLIENT);
|
|
95
105
|
} else {
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -40,12 +40,16 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
40
40
|
matchingKey: getMatching(settings.core.key),
|
|
41
41
|
splitFiltersValidation: settings.sync.__splitFiltersValidation,
|
|
42
42
|
|
|
43
|
-
//
|
|
44
|
-
|
|
43
|
+
// ATM, only used by PluggableStorage
|
|
44
|
+
mode: settings.mode,
|
|
45
|
+
|
|
46
|
+
// Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined
|
|
47
|
+
// or only instantiates submitters, and therefore it is not able to emit readiness events.
|
|
48
|
+
onReadyCb: (error) => {
|
|
45
49
|
if (error) return; // don't emit SDK_READY if storage failed to connect.
|
|
46
50
|
readinessManager.splits.emit(SDK_SPLITS_ARRIVED);
|
|
47
51
|
readinessManager.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
48
|
-
}
|
|
52
|
+
},
|
|
49
53
|
metadata: metadataBuilder(settings),
|
|
50
54
|
log
|
|
51
55
|
};
|
package/src/services/splitApi.ts
CHANGED
|
@@ -16,7 +16,10 @@ function userKeyToQueryParam(userKey: string) {
|
|
|
16
16
|
* @param settings validated settings object
|
|
17
17
|
* @param platform object containing environment-specific `getFetch` and `getOptions` dependencies
|
|
18
18
|
*/
|
|
19
|
-
export function splitApiFactory(
|
|
19
|
+
export function splitApiFactory(
|
|
20
|
+
settings: Pick<ISettings, 'urls' | 'sync' | 'log' | 'version' | 'runtime' | 'core'>,
|
|
21
|
+
platform: Pick<IPlatform, 'getFetch' | 'getOptions'>
|
|
22
|
+
): ISplitApi {
|
|
20
23
|
|
|
21
24
|
const urls = settings.urls;
|
|
22
25
|
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory';
|
|
2
2
|
import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory';
|
|
3
3
|
import EventsCacheInMemory from '../inMemory/EventsCacheInMemory';
|
|
4
|
-
import { IStorageFactoryParams,
|
|
4
|
+
import { IStorageFactoryParams, IStorageSync, IStorageSyncFactory } from '../types';
|
|
5
5
|
import { validatePrefix } from '../KeyBuilder';
|
|
6
6
|
import KeyBuilderCS from '../KeyBuilderCS';
|
|
7
7
|
import { isLocalStorageAvailable } from '../../utils/env/isLocalStorageAvailable';
|
|
@@ -25,7 +25,7 @@ export function InLocalStorage(options: InLocalStorageOptions = {}): IStorageSyn
|
|
|
25
25
|
|
|
26
26
|
const prefix = validatePrefix(options.prefix);
|
|
27
27
|
|
|
28
|
-
function InLocalStorageCSFactory(params: IStorageFactoryParams):
|
|
28
|
+
function InLocalStorageCSFactory(params: IStorageFactoryParams): IStorageSync {
|
|
29
29
|
|
|
30
30
|
// Fallback to InMemoryStorage if LocalStorage API is not available
|
|
31
31
|
if (!isLocalStorageAvailable()) {
|
|
@@ -13,9 +13,6 @@ import { STORAGE_MEMORY } from '../../utils/constants';
|
|
|
13
13
|
*/
|
|
14
14
|
export function InMemoryStorageFactory(params: IStorageFactoryParams): IStorageSync {
|
|
15
15
|
|
|
16
|
-
// InMemory storage is always ready
|
|
17
|
-
if (params.onReadyCb) setTimeout(params.onReadyCb);
|
|
18
|
-
|
|
19
16
|
return {
|
|
20
17
|
splits: new SplitsCacheInMemory(),
|
|
21
18
|
segments: new SegmentsCacheInMemory(),
|
|
@@ -2,7 +2,7 @@ import SplitsCacheInMemory from './SplitsCacheInMemory';
|
|
|
2
2
|
import MySegmentsCacheInMemory from './MySegmentsCacheInMemory';
|
|
3
3
|
import ImpressionsCacheInMemory from './ImpressionsCacheInMemory';
|
|
4
4
|
import EventsCacheInMemory from './EventsCacheInMemory';
|
|
5
|
-
import {
|
|
5
|
+
import { IStorageSync, IStorageFactoryParams } from '../types';
|
|
6
6
|
import ImpressionCountsCacheInMemory from './ImpressionCountsCacheInMemory';
|
|
7
7
|
import { STORAGE_MEMORY } from '../../utils/constants';
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ import { STORAGE_MEMORY } from '../../utils/constants';
|
|
|
11
11
|
*
|
|
12
12
|
* @param params parameters required by EventsCacheSync
|
|
13
13
|
*/
|
|
14
|
-
export function InMemoryStorageCSFactory(params: IStorageFactoryParams):
|
|
14
|
+
export function InMemoryStorageCSFactory(params: IStorageFactoryParams): IStorageSync {
|
|
15
15
|
|
|
16
16
|
return {
|
|
17
17
|
splits: new SplitsCacheInMemory(),
|
|
@@ -30,7 +30,7 @@ export function InRedisStorage(options: InRedisStorageOptions = {}): IStorageAsy
|
|
|
30
30
|
|
|
31
31
|
// subscription to Redis connect event in order to emit SDK_READY event on consumer mode
|
|
32
32
|
redisClient.on('connect', () => {
|
|
33
|
-
|
|
33
|
+
onReadyCb();
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
return {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IPluggableStorageWrapper, IEventsCacheAsync } from '../types';
|
|
2
2
|
import { IMetadata } from '../../dtos/types';
|
|
3
3
|
import { SplitIO } from '../../types';
|
|
4
4
|
import { ILogger } from '../../logger/types';
|
|
@@ -8,11 +8,11 @@ import { StoredEventWithMetadata } from '../../sync/submitters/types';
|
|
|
8
8
|
export class EventsCachePluggable implements IEventsCacheAsync {
|
|
9
9
|
|
|
10
10
|
private readonly log: ILogger;
|
|
11
|
-
private readonly wrapper:
|
|
11
|
+
private readonly wrapper: IPluggableStorageWrapper;
|
|
12
12
|
private readonly key: string;
|
|
13
13
|
private readonly metadata: IMetadata;
|
|
14
14
|
|
|
15
|
-
constructor(log: ILogger, key: string, wrapper:
|
|
15
|
+
constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, metadata: IMetadata) {
|
|
16
16
|
this.log = log;
|
|
17
17
|
this.key = key;
|
|
18
18
|
this.wrapper = wrapper;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IPluggableStorageWrapper, IImpressionsCacheAsync } from '../types';
|
|
2
2
|
import { IMetadata } from '../../dtos/types';
|
|
3
3
|
import { ImpressionDTO } from '../../types';
|
|
4
4
|
import { ILogger } from '../../logger/types';
|
|
@@ -8,10 +8,10 @@ export class ImpressionsCachePluggable implements IImpressionsCacheAsync {
|
|
|
8
8
|
|
|
9
9
|
private readonly log: ILogger;
|
|
10
10
|
private readonly key: string;
|
|
11
|
-
private readonly wrapper:
|
|
11
|
+
private readonly wrapper: IPluggableStorageWrapper;
|
|
12
12
|
private readonly metadata: IMetadata;
|
|
13
13
|
|
|
14
|
-
constructor(log: ILogger, key: string, wrapper:
|
|
14
|
+
constructor(log: ILogger, key: string, wrapper: IPluggableStorageWrapper, metadata: IMetadata) {
|
|
15
15
|
this.log = log;
|
|
16
16
|
this.key = key;
|
|
17
17
|
this.wrapper = wrapper;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/* eslint-disable no-unused-vars */
|
|
3
3
|
import { isNaNNumber } from '../../utils/lang';
|
|
4
4
|
import KeyBuilderSS from '../KeyBuilderSS';
|
|
5
|
-
import {
|
|
5
|
+
import { IPluggableStorageWrapper, ISegmentsCacheAsync } from '../types';
|
|
6
6
|
import { ILogger } from '../../logger/types';
|
|
7
7
|
import { LOG_PREFIX } from './constants';
|
|
8
8
|
import { _Set } from '../../utils/lang/sets';
|
|
@@ -14,9 +14,9 @@ export class SegmentsCachePluggable implements ISegmentsCacheAsync {
|
|
|
14
14
|
|
|
15
15
|
private readonly log: ILogger;
|
|
16
16
|
private readonly keys: KeyBuilderSS;
|
|
17
|
-
private readonly wrapper:
|
|
17
|
+
private readonly wrapper: IPluggableStorageWrapper;
|
|
18
18
|
|
|
19
|
-
constructor(log: ILogger, keys: KeyBuilderSS, wrapper:
|
|
19
|
+
constructor(log: ILogger, keys: KeyBuilderSS, wrapper: IPluggableStorageWrapper) {
|
|
20
20
|
this.log = log;
|
|
21
21
|
this.keys = keys;
|
|
22
22
|
this.wrapper = wrapper;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isFiniteNumber, isNaNNumber } from '../../utils/lang';
|
|
2
2
|
import KeyBuilder from '../KeyBuilder';
|
|
3
|
-
import {
|
|
3
|
+
import { IPluggableStorageWrapper } from '../types';
|
|
4
4
|
import { ILogger } from '../../logger/types';
|
|
5
5
|
import { ISplit } from '../../dtos/types';
|
|
6
6
|
import { LOG_PREFIX } from './constants';
|
|
@@ -13,15 +13,15 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
13
13
|
|
|
14
14
|
private readonly log: ILogger;
|
|
15
15
|
private readonly keys: KeyBuilder;
|
|
16
|
-
private readonly wrapper:
|
|
16
|
+
private readonly wrapper: IPluggableStorageWrapper;
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Create a SplitsCache that uses a
|
|
19
|
+
* Create a SplitsCache that uses a storage wrapper.
|
|
20
20
|
* @param log Logger instance.
|
|
21
21
|
* @param keys Key builder.
|
|
22
22
|
* @param wrapper Adapted wrapper storage.
|
|
23
23
|
*/
|
|
24
|
-
constructor(log: ILogger, keys: KeyBuilder, wrapper:
|
|
24
|
+
constructor(log: ILogger, keys: KeyBuilder, wrapper: IPluggableStorageWrapper) {
|
|
25
25
|
super();
|
|
26
26
|
this.log = log;
|
|
27
27
|
this.keys = keys;
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IPluggableStorageWrapper } from '../types';
|
|
2
2
|
import { startsWith, toNumber } from '../../utils/lang';
|
|
3
3
|
import { ISet, setToArray, _Set } from '../../utils/lang/sets';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Creates a
|
|
6
|
+
* Creates a IPluggableStorageWrapper implementation that stores items in memory.
|
|
7
7
|
* The `_cache` property is the object were items are stored.
|
|
8
8
|
* Intended for testing purposes.
|
|
9
|
+
*
|
|
10
|
+
* @param connDelay delay in millis for `connect` resolve. If not provided, `connect` resolves inmediatelly.
|
|
9
11
|
*/
|
|
10
|
-
export function inMemoryWrapperFactory():
|
|
12
|
+
export function inMemoryWrapperFactory(connDelay?: number): IPluggableStorageWrapper & { _cache: Record<string, string | string[] | ISet<string>>, _setConnDelay(connDelay: number): void } {
|
|
11
13
|
|
|
12
14
|
let _cache: Record<string, string | string[] | ISet<string>> = {};
|
|
15
|
+
let _connDelay = connDelay;
|
|
13
16
|
|
|
14
17
|
return {
|
|
15
18
|
/** Holds items (for key-value operations), list of items (for list operations) and set of items (for set operations) */
|
|
@@ -109,8 +112,19 @@ export function inMemoryWrapperFactory(): ICustomStorageWrapper & { _cache: Reco
|
|
|
109
112
|
return Promise.reject('key is not a set');
|
|
110
113
|
},
|
|
111
114
|
|
|
112
|
-
// always connects and
|
|
113
|
-
connect() {
|
|
114
|
-
|
|
115
|
+
// always connects and disconnects
|
|
116
|
+
connect() {
|
|
117
|
+
if (typeof _connDelay === 'number') {
|
|
118
|
+
return new Promise(res => setTimeout(res, _connDelay));
|
|
119
|
+
} else {
|
|
120
|
+
return Promise.resolve();
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
disconnect() { return Promise.resolve(); },
|
|
124
|
+
|
|
125
|
+
// for testing
|
|
126
|
+
_setConnDelay(connDelay: number) {
|
|
127
|
+
_connDelay = connDelay;
|
|
128
|
+
}
|
|
115
129
|
};
|
|
116
130
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IPluggableStorageWrapper, IStorageAsync, IStorageAsyncFactory, IStorageFactoryParams } from '../types';
|
|
2
2
|
|
|
3
3
|
import KeyBuilderSS from '../KeyBuilderSS';
|
|
4
4
|
import { SplitsCachePluggable } from './SplitsCachePluggable';
|
|
@@ -8,14 +8,17 @@ import { EventsCachePluggable } from './EventsCachePluggable';
|
|
|
8
8
|
import { wrapperAdapter, METHODS_TO_PROMISE_WRAP } from './wrapperAdapter';
|
|
9
9
|
import { isObject } from '../../utils/lang';
|
|
10
10
|
import { validatePrefix } from '../KeyBuilder';
|
|
11
|
-
import {
|
|
11
|
+
import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
12
|
+
import ImpressionsCacheInMemory from '../inMemory/ImpressionsCacheInMemory';
|
|
13
|
+
import EventsCacheInMemory from '../inMemory/EventsCacheInMemory';
|
|
14
|
+
import ImpressionCountsCacheInMemory from '../inMemory/ImpressionCountsCacheInMemory';
|
|
12
15
|
|
|
13
|
-
const NO_VALID_WRAPPER = 'Expecting
|
|
16
|
+
const NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
14
17
|
const NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
15
18
|
|
|
16
19
|
export interface PluggableStorageOptions {
|
|
17
20
|
prefix?: string
|
|
18
|
-
wrapper:
|
|
21
|
+
wrapper: IPluggableStorageWrapper
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
/**
|
|
@@ -32,6 +35,25 @@ function validatePluggableStorageOptions(options: any) {
|
|
|
32
35
|
if (missingMethods.length) throw new Error(`${NO_VALID_WRAPPER_INTERFACE} The following methods are missing or invalid: ${missingMethods}`);
|
|
33
36
|
}
|
|
34
37
|
|
|
38
|
+
// subscription to wrapper connect event in order to emit SDK_READY event
|
|
39
|
+
function wrapperConnect(wrapper: IPluggableStorageWrapper, onReadyCb: (error?: any) => void) {
|
|
40
|
+
wrapper.connect().then(() => {
|
|
41
|
+
onReadyCb();
|
|
42
|
+
}).catch((e) => {
|
|
43
|
+
onReadyCb(e || new Error('Error connecting wrapper'));
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Async return type in `client.track` method on consumer partial mode
|
|
48
|
+
// No need to promisify impressions cache
|
|
49
|
+
function promisifyEventsTrack(events: any) {
|
|
50
|
+
const origTrack = events.track;
|
|
51
|
+
events.track = function () {
|
|
52
|
+
return Promise.resolve(origTrack.apply(this, arguments));
|
|
53
|
+
};
|
|
54
|
+
return events;
|
|
55
|
+
}
|
|
56
|
+
|
|
35
57
|
/**
|
|
36
58
|
* Pluggable storage factory for consumer server-side & client-side SplitFactory.
|
|
37
59
|
*/
|
|
@@ -41,31 +63,39 @@ export function PluggableStorage(options: PluggableStorageOptions): IStorageAsyn
|
|
|
41
63
|
|
|
42
64
|
const prefix = validatePrefix(options.prefix);
|
|
43
65
|
|
|
44
|
-
function PluggableStorageFactory({ log, metadata, onReadyCb }: IStorageFactoryParams): IStorageAsync {
|
|
66
|
+
function PluggableStorageFactory({ log, metadata, onReadyCb, mode, eventsQueueSize, optimize }: IStorageFactoryParams): IStorageAsync {
|
|
45
67
|
const keys = new KeyBuilderSS(prefix, metadata);
|
|
46
68
|
const wrapper = wrapperAdapter(log, options.wrapper);
|
|
69
|
+
const isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
|
|
47
70
|
|
|
48
|
-
//
|
|
49
|
-
wrapper
|
|
50
|
-
if (onReadyCb) onReadyCb();
|
|
51
|
-
}).catch((e) => {
|
|
52
|
-
if (onReadyCb) onReadyCb(e || new Error('Error connecting wrapper'));
|
|
53
|
-
});
|
|
71
|
+
// Connects to wrapper and emits SDK_READY event on main client
|
|
72
|
+
wrapperConnect(wrapper, onReadyCb);
|
|
54
73
|
|
|
55
74
|
return {
|
|
56
75
|
splits: new SplitsCachePluggable(log, keys, wrapper),
|
|
57
76
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
58
|
-
impressions: new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
59
|
-
|
|
77
|
+
impressions: isPartialConsumer ? new ImpressionsCacheInMemory() : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
78
|
+
impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
79
|
+
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
60
80
|
// @TODO add telemetry cache when required
|
|
61
81
|
|
|
62
|
-
// Disconnect the underlying storage
|
|
82
|
+
// Disconnect the underlying storage
|
|
63
83
|
destroy() {
|
|
64
|
-
return wrapper.
|
|
84
|
+
return wrapper.disconnect();
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// emits SDK_READY event on shared clients and returns a reference to the storage
|
|
88
|
+
shared(_, onReadyCb) {
|
|
89
|
+
wrapperConnect(wrapper, onReadyCb);
|
|
90
|
+
return {
|
|
91
|
+
...this,
|
|
92
|
+
// no-op destroy, to disconnect the wrapper only when the main client is destroyed
|
|
93
|
+
destroy() { }
|
|
94
|
+
};
|
|
65
95
|
}
|
|
66
96
|
};
|
|
67
97
|
}
|
|
68
98
|
|
|
69
|
-
PluggableStorageFactory.type =
|
|
99
|
+
PluggableStorageFactory.type = STORAGE_PLUGGABLE;
|
|
70
100
|
return PluggableStorageFactory;
|
|
71
101
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ILogger } from '../../logger/types';
|
|
2
|
-
import {
|
|
2
|
+
import { IPluggableStorageWrapper } from '../types';
|
|
3
3
|
import { LOG_PREFIX } from './constants';
|
|
4
4
|
|
|
5
5
|
export const METHODS_TO_PROMISE_WRAP: string[] = [
|
|
@@ -19,18 +19,18 @@ export const METHODS_TO_PROMISE_WRAP: string[] = [
|
|
|
19
19
|
'removeItems',
|
|
20
20
|
'getItems',
|
|
21
21
|
'connect',
|
|
22
|
-
'
|
|
22
|
+
'disconnect'
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Adapter of the
|
|
26
|
+
* Adapter of the Pluggable Storage Wrapper.
|
|
27
27
|
* Used to handle exceptions as rejected promises, in order to simplify the error handling on storages.
|
|
28
28
|
*
|
|
29
29
|
* @param log logger instance
|
|
30
|
-
* @param wrapper
|
|
30
|
+
* @param wrapper storage wrapper to adapt
|
|
31
31
|
* @returns an adapted version of the given storage wrapper
|
|
32
32
|
*/
|
|
33
|
-
export function wrapperAdapter(log: ILogger, wrapper:
|
|
33
|
+
export function wrapperAdapter(log: ILogger, wrapper: IPluggableStorageWrapper): IPluggableStorageWrapper {
|
|
34
34
|
|
|
35
35
|
const wrapperAdapter: Record<string, Function> = {};
|
|
36
36
|
|