@splitsoftware/splitio-commons 3.1.1-rc.10 → 3.1.1-rc.12
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 +3 -0
- package/cjs/evaluator/condition/index.js +2 -2
- package/cjs/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +5 -8
- package/cjs/evaluator/index.js +81 -71
- package/cjs/listeners/browser.js +2 -1
- package/cjs/listeners/node.js +4 -4
- package/cjs/logger/constants.js +15 -20
- package/cjs/logger/messages/debug.js +5 -6
- package/cjs/logger/messages/error.js +3 -3
- package/cjs/logger/messages/info.js +3 -4
- package/cjs/logger/messages/warn.js +6 -7
- package/cjs/platform/browser.js +15 -0
- package/cjs/platform/getEventSource/browser.js +8 -0
- package/cjs/platform/getEventSource/eventsource.js +480 -0
- package/cjs/platform/getEventSource/node.js +26 -0
- package/cjs/platform/getFetch/browser.js +9 -0
- package/cjs/platform/getFetch/node.js +27 -0
- package/cjs/platform/getOptions/node.js +25 -0
- package/cjs/platform/node.js +17 -0
- package/cjs/presets/serverSide.js +2 -2
- package/cjs/readiness/constants.js +5 -4
- package/cjs/readiness/readinessManager.js +23 -23
- package/cjs/readiness/sdkReadinessManager.js +1 -17
- package/cjs/sdkClient/client.js +24 -50
- package/cjs/sdkClient/clientInputValidation.js +6 -17
- package/cjs/sdkClient/sdkClient.js +3 -60
- package/cjs/sdkClient/sdkLifecycle.js +74 -0
- package/cjs/sdkClient/trackMethod.js +56 -0
- package/cjs/sdkFactory/index.js +11 -15
- package/cjs/sdkManager/index.js +5 -5
- package/cjs/services/authProvider.js +80 -0
- package/cjs/services/decorateHeaders.js +2 -1
- package/cjs/{sync/streaming/AuthClient/index.js → services/fetchAuth.js} +15 -16
- package/cjs/services/secureSplitHttpClient.js +36 -0
- package/cjs/services/serviceApi.js +147 -0
- package/cjs/services/splitApi.js +24 -10
- package/cjs/services/splitHttpClient.js +3 -5
- package/cjs/storages/AbstractDefinitionsCacheAsync.js +51 -0
- package/cjs/storages/AbstractDefinitionsCacheSync.js +76 -0
- package/cjs/storages/KeyBuilder.js +6 -6
- package/cjs/storages/KeyBuilderCS.js +5 -5
- package/cjs/storages/KeyBuilderSS.js +2 -2
- package/cjs/storages/getRolloutPlan.js +3 -3
- package/cjs/storages/inLocalStorage/DefinitionsCacheInLocal.js +195 -0
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +1 -2
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +12 -11
- package/cjs/storages/inLocalStorage/index.js +6 -6
- package/cjs/storages/inLocalStorage/validateCache.js +3 -3
- package/cjs/storages/inMemory/DefinitionsCacheInMemory.js +122 -0
- package/cjs/storages/inMemory/InMemoryStorage.js +4 -4
- package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -5
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +7 -6
- package/cjs/storages/inMemory/SegmentsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +5 -5
- package/cjs/storages/inRedis/{SplitsCacheInRedis.js → DefinitionsCacheInRedis.js} +79 -77
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +4 -2
- package/cjs/storages/inRedis/index.js +2 -2
- package/cjs/storages/pluggable/DefinitionsCachePluggable.js +214 -0
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +4 -2
- package/cjs/storages/pluggable/index.js +2 -2
- package/cjs/storages/setRolloutPlan.js +4 -4
- package/cjs/storages/utils.js +1 -0
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +5 -5
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +24 -15
- package/cjs/sync/polling/pollingManagerCS.js +86 -81
- package/cjs/sync/polling/pollingManagerSS.js +44 -36
- package/cjs/sync/polling/syncTasks/definitionsSyncTask.js +13 -0
- package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +2 -3
- package/cjs/sync/polling/updaters/definitionChangesUpdater.js +207 -0
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +3 -3
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +6 -6
- package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +1 -1
- package/cjs/sync/streaming/SSEHandler/index.js +2 -1
- package/cjs/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +19 -20
- package/cjs/sync/streaming/constants.js +4 -3
- package/cjs/sync/streaming/parseUtils.js +14 -14
- package/cjs/sync/streaming/pushManager.js +22 -26
- package/cjs/sync/submitters/eventsSubmitter.js +1 -1
- package/cjs/sync/submitters/impressionCountsSubmitter.js +1 -1
- package/cjs/sync/submitters/impressionsSubmitter.js +5 -4
- package/cjs/sync/submitters/telemetrySubmitter.js +3 -4
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +1 -1
- package/cjs/sync/syncManagerOnline.js +5 -5
- package/cjs/utils/Backoff.js +35 -3
- package/cjs/utils/EventEmitter.js +391 -0
- package/cjs/utils/filter/bloomFilter.js +37 -0
- package/cjs/utils/inputValidation/{split.js → definition.js} +10 -10
- package/cjs/utils/inputValidation/{splitExistence.js → definitionExistence.js} +6 -6
- package/cjs/utils/inputValidation/definitions.js +25 -0
- package/cjs/utils/inputValidation/index.js +7 -7
- package/cjs/utils/inputValidation/isOperational.js +4 -4
- package/cjs/utils/inputValidation/target.js +22 -0
- package/cjs/utils/labels/index.js +3 -3
- package/cjs/utils/settingsValidation/index.js +2 -0
- package/cjs/utils/settingsValidation/splitFilters.js +3 -3
- package/cjs/utils/settingsValidation/url.js +6 -2
- package/esm/evaluator/condition/index.js +2 -2
- package/esm/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +5 -8
- package/esm/evaluator/index.js +80 -71
- package/esm/listeners/browser.js +2 -1
- package/esm/listeners/node.js +4 -4
- package/esm/logger/constants.js +12 -17
- package/esm/logger/messages/debug.js +5 -6
- package/esm/logger/messages/error.js +3 -3
- package/esm/logger/messages/info.js +3 -4
- package/esm/logger/messages/warn.js +6 -7
- package/esm/platform/browser.js +12 -0
- package/esm/platform/getEventSource/browser.js +4 -0
- package/esm/platform/getEventSource/eventsource.js +480 -0
- package/esm/platform/getEventSource/node.js +20 -0
- package/esm/platform/getFetch/browser.js +5 -0
- package/esm/platform/getFetch/node.js +22 -0
- package/esm/platform/getOptions/node.js +20 -0
- package/esm/platform/node.js +14 -0
- package/esm/presets/serverSide.js +2 -2
- package/esm/readiness/constants.js +4 -3
- package/esm/readiness/readinessManager.js +24 -24
- package/esm/readiness/sdkReadinessManager.js +2 -18
- package/esm/sdkClient/client.js +23 -50
- package/esm/sdkClient/clientInputValidation.js +8 -19
- package/esm/sdkClient/sdkClient.js +3 -60
- package/esm/sdkClient/sdkLifecycle.js +70 -0
- package/esm/sdkClient/trackMethod.js +52 -0
- package/esm/sdkFactory/index.js +12 -16
- package/esm/sdkManager/index.js +6 -6
- package/esm/services/authProvider.js +76 -0
- package/esm/services/decorateHeaders.js +2 -1
- package/esm/{sync/streaming/AuthClient/index.js → services/fetchAuth.js} +13 -13
- package/esm/services/secureSplitHttpClient.js +32 -0
- package/esm/services/serviceApi.js +143 -0
- package/esm/services/splitApi.js +24 -10
- package/esm/services/splitHttpClient.js +3 -5
- package/esm/storages/AbstractDefinitionsCacheAsync.js +48 -0
- package/esm/storages/AbstractDefinitionsCacheSync.js +71 -0
- package/esm/storages/KeyBuilder.js +6 -6
- package/esm/storages/KeyBuilderCS.js +5 -5
- package/esm/storages/KeyBuilderSS.js +2 -2
- package/esm/storages/getRolloutPlan.js +3 -3
- package/esm/storages/inLocalStorage/DefinitionsCacheInLocal.js +192 -0
- package/esm/storages/inLocalStorage/MySegmentsCacheInLocal.js +1 -2
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +9 -8
- package/esm/storages/inLocalStorage/index.js +6 -6
- package/esm/storages/inLocalStorage/validateCache.js +3 -3
- package/esm/storages/inMemory/DefinitionsCacheInMemory.js +119 -0
- package/esm/storages/inMemory/InMemoryStorage.js +4 -4
- package/esm/storages/inMemory/InMemoryStorageCS.js +5 -5
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +4 -3
- package/esm/storages/inMemory/SegmentsCacheInMemory.js +1 -1
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +5 -5
- package/esm/storages/inRedis/{SplitsCacheInRedis.js → DefinitionsCacheInRedis.js} +78 -76
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +4 -2
- package/esm/storages/inRedis/index.js +2 -2
- package/esm/storages/pluggable/DefinitionsCachePluggable.js +211 -0
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +4 -2
- package/esm/storages/pluggable/index.js +2 -2
- package/esm/storages/setRolloutPlan.js +4 -4
- package/esm/storages/utils.js +1 -0
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +6 -6
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +25 -16
- package/esm/sync/polling/pollingManagerCS.js +87 -82
- package/esm/sync/polling/pollingManagerSS.js +44 -36
- package/esm/sync/polling/syncTasks/definitionsSyncTask.js +9 -0
- package/esm/sync/polling/syncTasks/segmentsSyncTask.js +2 -3
- package/esm/sync/polling/updaters/definitionChangesUpdater.js +201 -0
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -3
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +7 -7
- package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +2 -2
- package/esm/sync/streaming/SSEHandler/index.js +3 -2
- package/esm/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +19 -20
- package/esm/sync/streaming/constants.js +3 -2
- package/esm/sync/streaming/parseUtils.js +11 -11
- package/esm/sync/streaming/pushManager.js +24 -28
- package/esm/sync/submitters/eventsSubmitter.js +1 -1
- package/esm/sync/submitters/impressionCountsSubmitter.js +1 -1
- package/esm/sync/submitters/impressionsSubmitter.js +5 -4
- package/esm/sync/submitters/telemetrySubmitter.js +3 -4
- package/esm/sync/submitters/uniqueKeysSubmitter.js +1 -1
- package/esm/sync/syncManagerOnline.js +4 -4
- package/esm/utils/Backoff.js +35 -3
- package/esm/utils/EventEmitter.js +387 -0
- package/esm/utils/filter/bloomFilter.js +33 -0
- package/esm/utils/inputValidation/{split.js → definition.js} +8 -8
- package/esm/utils/inputValidation/definitionExistence.js +15 -0
- package/esm/utils/inputValidation/definitions.js +21 -0
- package/esm/utils/inputValidation/index.js +3 -3
- package/esm/utils/inputValidation/isOperational.js +4 -4
- package/esm/utils/inputValidation/target.js +18 -0
- package/esm/utils/labels/index.js +2 -2
- package/esm/utils/settingsValidation/index.js +2 -0
- package/esm/utils/settingsValidation/splitFilters.js +3 -3
- package/esm/utils/settingsValidation/url.js +6 -2
- package/package.json +1 -1
- package/src/dtos/types.ts +4 -4
- package/src/services/authProvider.ts +2 -2
- package/src/{sync/streaming/AuthClient/index.ts → services/fetchAuth.ts} +3 -4
- package/src/services/splitApi.ts +1 -1
- package/src/services/types.ts +21 -4
- package/src/storages/AbstractDefinitionsCacheAsync.ts +3 -3
- package/src/storages/AbstractDefinitionsCacheSync.ts +2 -2
- package/src/storages/inLocalStorage/DefinitionsCacheInLocal.ts +2 -2
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +3 -3
- package/src/storages/inMemory/DefinitionsCacheInMemory.ts +2 -2
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +2 -2
- package/src/storages/inRedis/DefinitionsCacheInRedis.ts +6 -4
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +7 -5
- package/src/storages/pluggable/DefinitionsCachePluggable.ts +4 -2
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +5 -3
- package/src/storages/types.ts +6 -6
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +5 -5
- package/src/sync/polling/types.ts +22 -1
- package/src/sync/polling/updaters/definitionChangesUpdater.ts +26 -20
- package/src/sync/streaming/SSEClient/index.ts +1 -1
- package/src/sync/streaming/SSEClient/types.ts +1 -1
- package/src/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.ts +15 -11
- package/src/sync/streaming/UpdateWorkers/types.ts +6 -0
- package/src/sync/streaming/parseUtils.ts +3 -4
- package/src/sync/streaming/pushManager.ts +4 -3
- package/cjs/storages/AbstractSplitsCacheAsync.js +0 -51
- package/cjs/storages/AbstractSplitsCacheSync.js +0 -76
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -194
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -121
- package/cjs/storages/pluggable/SplitsCachePluggable.js +0 -212
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +0 -13
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +0 -200
- package/cjs/sync/streaming/AuthClient/types.js +0 -2
- package/cjs/utils/inputValidation/splits.js +0 -25
- package/esm/storages/AbstractSplitsCacheAsync.js +0 -48
- package/esm/storages/AbstractSplitsCacheSync.js +0 -71
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -191
- package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -118
- package/esm/storages/pluggable/SplitsCachePluggable.js +0 -209
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +0 -9
- package/esm/sync/polling/updaters/splitChangesUpdater.js +0 -194
- package/esm/sync/streaming/AuthClient/types.js +0 -1
- package/esm/utils/inputValidation/splitExistence.js +0 -15
- package/esm/utils/inputValidation/splits.js +0 -21
- package/src/sync/streaming/AuthClient/types.ts +0 -19
package/esm/sdkFactory/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { eventTrackerFactory } from '../trackers/eventTracker';
|
|
|
4
4
|
import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
|
|
5
5
|
import { createLoggerAPI } from '../logger/sdkLogger';
|
|
6
6
|
import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
|
|
7
|
-
import {
|
|
7
|
+
import { SDK_DEFINITIONS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_DEFINITIONS_CACHE_LOADED } from '../readiness/constants';
|
|
8
8
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
9
9
|
import { setRolloutPlan } from '../storages/setRolloutPlan';
|
|
10
10
|
import { getMatching } from '../utils/key';
|
|
@@ -13,7 +13,7 @@ import { FallbackTreatmentsCalculator } from '../evaluator/fallbackTreatmentsCal
|
|
|
13
13
|
* Modular SDK factory
|
|
14
14
|
*/
|
|
15
15
|
export function sdkFactory(params) {
|
|
16
|
-
var settings = params.settings, platform = params.platform, storageFactory = params.storageFactory,
|
|
16
|
+
var settings = params.settings, platform = params.platform, storageFactory = params.storageFactory, serviceApiFactory = params.serviceApiFactory, extraProps = params.extraProps, syncManagerFactory = params.syncManagerFactory, integrationsManagerFactory = params.integrationsManagerFactory, sdkManagerFactory = params.sdkManagerFactory, sdkClientMethodFactory = params.sdkClientMethodFactory, lazyInit = params.lazyInit;
|
|
17
17
|
var log = settings.log, initialRolloutPlan = settings.initialRolloutPlan, key = settings.core.key;
|
|
18
18
|
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
|
|
19
19
|
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
|
|
@@ -27,35 +27,31 @@ export function sdkFactory(params) {
|
|
|
27
27
|
readiness.timeout();
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
readiness.
|
|
30
|
+
readiness.definitions.emit(SDK_DEFINITIONS_ARRIVED);
|
|
31
31
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
32
32
|
},
|
|
33
33
|
onReadyFromCacheCb: function () {
|
|
34
|
-
readiness.
|
|
34
|
+
readiness.definitions.emit(SDK_DEFINITIONS_CACHE_LOADED);
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
|
-
var
|
|
37
|
+
var fallbackCalculator = FallbackTreatmentsCalculator(settings.fallbackTreatments);
|
|
38
38
|
if (initialRolloutPlan) {
|
|
39
39
|
setRolloutPlan(log, initialRolloutPlan, storage, key && getMatching(key));
|
|
40
|
-
if (storage.
|
|
41
|
-
readiness.
|
|
40
|
+
if (storage.definitions.getChangeNumber() > -1)
|
|
41
|
+
readiness.definitions.emit(SDK_DEFINITIONS_CACHE_LOADED, { initialCacheLoad: false /* Not an initial load, cache exists */ });
|
|
42
42
|
}
|
|
43
43
|
var clients = {};
|
|
44
44
|
var telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
|
|
45
45
|
var integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings: settings, storage: storage, telemetryTracker: telemetryTracker });
|
|
46
46
|
var impressionsTracker = impressionsTrackerFactory(params, storage, integrationsManager);
|
|
47
47
|
var eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
|
|
48
|
-
//
|
|
49
|
-
var
|
|
50
|
-
var ctx = { clients: clients,
|
|
51
|
-
|
|
52
|
-
ctx.syncManager = syncManager;
|
|
53
|
-
// @TODO: move into platform, and call inside sdkClientFactory (if it's used only there)
|
|
54
|
-
var signalListener = SignalListener && new SignalListener(syncManager, settings, storage, splitApi);
|
|
55
|
-
ctx.signalListener = signalListener;
|
|
48
|
+
// serviceApi is used by SyncManager and Browser signal listener
|
|
49
|
+
var serviceApi = serviceApiFactory && serviceApiFactory(settings, platform, telemetryTracker);
|
|
50
|
+
var ctx = { clients: clients, serviceApi: serviceApi, eventTracker: eventTracker, impressionsTracker: impressionsTracker, telemetryTracker: telemetryTracker, sdkReadinessManager: sdkReadinessManager, readiness: readiness, settings: settings, storage: storage, platform: platform, fallbackCalculator: fallbackCalculator };
|
|
51
|
+
ctx.syncManager = syncManagerFactory && syncManagerFactory(ctx);
|
|
56
52
|
// SDK client and manager
|
|
57
53
|
var clientMethod = sdkClientMethodFactory(ctx);
|
|
58
|
-
var managerInstance = sdkManagerFactory(settings, storage.
|
|
54
|
+
var managerInstance = sdkManagerFactory(settings, storage.definitions, sdkReadinessManager);
|
|
59
55
|
function init() {
|
|
60
56
|
Object.keys(clients).map(function (key) { return clients[key].init(); });
|
|
61
57
|
}
|
package/esm/sdkManager/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { find } from '../utils/lang';
|
|
4
|
-
import {
|
|
4
|
+
import { validateDefinition, validateDefinitionExistence, validateIfOperational } from '../utils/inputValidation';
|
|
5
5
|
import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
6
6
|
import { SPLIT_FN_LABEL, SPLITS_FN_LABEL, NAMES_FN_LABEL } from '../utils/constants';
|
|
7
7
|
function collectTreatments(splitObject) {
|
|
@@ -50,18 +50,18 @@ export function sdkManagerFactory(settings, splits, _a) {
|
|
|
50
50
|
* Get the feature flag object corresponding to the given feature flag name if valid
|
|
51
51
|
*/
|
|
52
52
|
split: function (featureFlagName) {
|
|
53
|
-
var splitName =
|
|
53
|
+
var splitName = validateDefinition(log, featureFlagName, SPLIT_FN_LABEL);
|
|
54
54
|
if (!validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
|
|
55
55
|
return isAsync ? Promise.resolve(null) : null;
|
|
56
56
|
}
|
|
57
|
-
var split = splits.
|
|
57
|
+
var split = splits.get(splitName);
|
|
58
58
|
if (thenable(split)) {
|
|
59
59
|
return split.catch(function () { return null; }).then(function (result) {
|
|
60
|
-
|
|
60
|
+
validateDefinitionExistence(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
|
|
61
61
|
return objectToView(result);
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
validateDefinitionExistence(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
|
|
65
65
|
return objectToView(split);
|
|
66
66
|
},
|
|
67
67
|
/**
|
|
@@ -83,7 +83,7 @@ export function sdkManagerFactory(settings, splits, _a) {
|
|
|
83
83
|
if (!validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
|
|
84
84
|
return isAsync ? Promise.resolve([]) : [];
|
|
85
85
|
}
|
|
86
|
-
var splitNames = splits.
|
|
86
|
+
var splitNames = splits.getNames();
|
|
87
87
|
return thenable(splitNames) ?
|
|
88
88
|
splitNames.catch(function () { return []; }) : // handle possible rejections when using pluggable storage
|
|
89
89
|
splitNames;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { fetchAuthFactory } from './fetchAuth';
|
|
2
|
+
import { Backoff } from '../utils/Backoff';
|
|
3
|
+
import { LOG_PREFIX_SYNC_AUTH } from '../logger/constants';
|
|
4
|
+
import { TOKEN } from '../utils/constants';
|
|
5
|
+
var SKEW_SECONDS = 30;
|
|
6
|
+
function isExpired(credential) {
|
|
7
|
+
return Date.now() / 1000 + SKEW_SECONDS >= credential.decodedToken.exp;
|
|
8
|
+
}
|
|
9
|
+
function noCachedCredentialError() {
|
|
10
|
+
return new Error(LOG_PREFIX_SYNC_AUTH + 'stopped without a cached credential');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Factory of AuthProvider, which provides JWT credentials for authenticated HTTP requests.
|
|
14
|
+
* Credentials are fetched lazily on demand, cached in memory, and retried with backoff on failure.
|
|
15
|
+
*/
|
|
16
|
+
export function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
|
|
17
|
+
var urls = settings.urls, log = settings.log;
|
|
18
|
+
var fetchAuth = fetchAuthFactory(function () {
|
|
19
|
+
var url = "".concat(urls.auth, "/api/v3/auth?capabilities=config,aiconfig");
|
|
20
|
+
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN), false, true);
|
|
21
|
+
});
|
|
22
|
+
var backoff = new Backoff(fetchCredential);
|
|
23
|
+
var cachedCredential;
|
|
24
|
+
var inFlightPromise;
|
|
25
|
+
var stopped = false;
|
|
26
|
+
function fetchCredential() {
|
|
27
|
+
return fetchAuth().then(function (credential) {
|
|
28
|
+
log.info(LOG_PREFIX_SYNC_AUTH + 'credential fetched successfully');
|
|
29
|
+
cachedCredential = credential;
|
|
30
|
+
inFlightPromise = undefined;
|
|
31
|
+
backoff.reset();
|
|
32
|
+
return credential;
|
|
33
|
+
}).catch(function (error) {
|
|
34
|
+
// Avoid unnecessary retries after stop(): fall back to any cached credential, or reject
|
|
35
|
+
// (never resolve with `undefined`, which callers would otherwise send as an unauthenticated request).
|
|
36
|
+
if (stopped)
|
|
37
|
+
return cachedCredential || Promise.reject(noCachedCredentialError());
|
|
38
|
+
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
|
|
39
|
+
log.error(LOG_PREFIX_SYNC_AUTH + 'non-retryable error fetching credential (status ' + error.statusCode + '): ' + error.message);
|
|
40
|
+
inFlightPromise = undefined;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
log.warn(LOG_PREFIX_SYNC_AUTH + 'credential fetch failed (attempt ' + (backoff.attempts + 1) + '). Error: ' + error.message);
|
|
44
|
+
return backoff.scheduleCallAsync();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
credential: function () {
|
|
49
|
+
if (cachedCredential && !isExpired(cachedCredential)) {
|
|
50
|
+
return Promise.resolve(cachedCredential);
|
|
51
|
+
}
|
|
52
|
+
// Reuse any fetch already in flight (e.g. one started just before stop()), instead of firing a redundant one.
|
|
53
|
+
if (inFlightPromise)
|
|
54
|
+
return inFlightPromise;
|
|
55
|
+
// No cached credential to fall back to and stopped: reject so callers don't send an unauthenticated request.
|
|
56
|
+
if (stopped && !cachedCredential) {
|
|
57
|
+
return Promise.reject(noCachedCredentialError());
|
|
58
|
+
}
|
|
59
|
+
if (cachedCredential)
|
|
60
|
+
log.debug(LOG_PREFIX_SYNC_AUTH + 'cached credential expired');
|
|
61
|
+
// If stopped, this is a last, bounded refetch: fetchCredential()'s catch short-circuits (no backoff retry)
|
|
62
|
+
// once stopped, so it's a single attempt that falls back to the stale cached credential on failure.
|
|
63
|
+
return inFlightPromise = fetchCredential();
|
|
64
|
+
},
|
|
65
|
+
invalidate: function () {
|
|
66
|
+
cachedCredential = undefined;
|
|
67
|
+
},
|
|
68
|
+
stop: function () {
|
|
69
|
+
stopped = true;
|
|
70
|
+
// Keep any in-flight fetch and already-cached credential usable (e.g. by a final flush on destroy).
|
|
71
|
+
// If a retry is only pending (not actively in flight), settle it immediately instead of waiting out the
|
|
72
|
+
// backoff delay: with the cached credential if there is one, or rejected otherwise.
|
|
73
|
+
backoff.reset(cachedCredential ? { value: cachedCredential } : { error: noCachedCredentialError() });
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { objectAssign } from '
|
|
2
|
-
import {
|
|
3
|
-
import { decodeJWTtoken } from '../../../utils/jwt';
|
|
4
|
-
import { hash } from '../../../utils/murmur3/murmur3';
|
|
1
|
+
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
|
+
import { decodeJWTtoken } from '../utils/jwt';
|
|
5
3
|
/**
|
|
6
4
|
* Factory of authentication function.
|
|
7
5
|
*
|
|
8
|
-
* @param fetchAuth -
|
|
6
|
+
* @param fetchAuth - /auth endpoint
|
|
9
7
|
*/
|
|
10
|
-
export function
|
|
8
|
+
export function fetchAuthFactory(fetchAuth) {
|
|
11
9
|
/**
|
|
12
10
|
* Run authentication requests to Auth Server, and returns a promise that resolves with the decoded JTW token.
|
|
13
11
|
* @param userKeys - set of user Keys to track membership updates. It is undefined for server-side API.
|
|
@@ -21,18 +19,20 @@ export function authenticateFactory(fetchAuth) {
|
|
|
21
19
|
if (typeof decodedToken.iat !== 'number' || typeof decodedToken.exp !== 'number')
|
|
22
20
|
throw new Error('token properties "issuedAt" (iat) or "expiration" (exp) are missing or invalid');
|
|
23
21
|
var channels = JSON.parse(decodedToken['x-ably-capability']);
|
|
24
|
-
|
|
22
|
+
var credential = objectAssign({
|
|
25
23
|
decodedToken: decodedToken,
|
|
26
24
|
channels: channels
|
|
27
25
|
}, json);
|
|
26
|
+
// The `/api/v3/auth` endpoint nests the streaming settings under `config.streaming`. Normalize
|
|
27
|
+
// them into the flat `pushEnabled`/`connDelay` properties used by the PushManager.
|
|
28
|
+
var streaming = credential.config && credential.config.streaming;
|
|
29
|
+
if (streaming) {
|
|
30
|
+
credential.pushEnabled = streaming.enabled;
|
|
31
|
+
credential.connDelay = streaming.delay;
|
|
32
|
+
}
|
|
33
|
+
return credential;
|
|
28
34
|
}
|
|
29
35
|
return json;
|
|
30
36
|
});
|
|
31
37
|
};
|
|
32
38
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Returns the hash of a given user key
|
|
35
|
-
*/
|
|
36
|
-
export function hashUserKey(userKey) {
|
|
37
|
-
return encodeToBase64(hash(userKey, 0).toString());
|
|
38
|
-
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { __assign } from "tslib";
|
|
2
|
+
/**
|
|
3
|
+
* Factory of Secure HTTP client, which authenticates requests using a JWT token.
|
|
4
|
+
* On 401 responses, invalidates the cached credential and retries once with a fresh token.
|
|
5
|
+
*
|
|
6
|
+
* @param splitHttpClient - `splitHttpClientFactory` to use for making requests
|
|
7
|
+
* @param authProvider - `authProviderFactory` to use for authentication
|
|
8
|
+
*/
|
|
9
|
+
export function secureSplitHttpClientFactory(splitHttpClient, authProvider) {
|
|
10
|
+
function makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, token) {
|
|
11
|
+
return splitHttpClient(url, token ? __assign(__assign({}, options), { headers: __assign(__assign({}, options === null || options === void 0 ? void 0 : options.headers), { Authorization: "Bearer ".concat(token) }) }) : options, latencyTracker, logErrorsAsInfo, newVersionHeader);
|
|
12
|
+
}
|
|
13
|
+
var httpClient = function (url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt) {
|
|
14
|
+
if (newVersionHeader === void 0) { newVersionHeader = true; }
|
|
15
|
+
if (useJwt === void 0) { useJwt = true; }
|
|
16
|
+
return authProvider.credential().then(function (credential) {
|
|
17
|
+
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? credential.token : undefined)
|
|
18
|
+
.catch(function (error) {
|
|
19
|
+
if (error.statusCode === 401) {
|
|
20
|
+
// retry once for 401, in case the token has just expired
|
|
21
|
+
authProvider.invalidate();
|
|
22
|
+
return authProvider.credential().then(function (newCredential) {
|
|
23
|
+
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? newCredential.token : undefined);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
httpClient.stop = function () { return authProvider.stop(); };
|
|
31
|
+
return httpClient;
|
|
32
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { splitHttpClientFactory } from './splitHttpClient';
|
|
2
|
+
import { objectAssign } from '../utils/lang/objectAssign';
|
|
3
|
+
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, SEGMENT, MEMBERSHIPS } from '../utils/constants';
|
|
4
|
+
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
|
|
5
|
+
import { authProviderFactory } from './authProvider';
|
|
6
|
+
import { secureSplitHttpClientFactory } from './secureSplitHttpClient';
|
|
7
|
+
var noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
|
|
8
|
+
/**
|
|
9
|
+
* Factory of ServiceApi objects, which group the collection of HTTP endpoints used by the SDKs
|
|
10
|
+
*
|
|
11
|
+
* @param settings - validated settings object
|
|
12
|
+
* @param platform - object containing environment-specific dependencies
|
|
13
|
+
* @param telemetryTracker - telemetry tracker
|
|
14
|
+
*/
|
|
15
|
+
export function serviceApiFactory(settings, platform, telemetryTracker) {
|
|
16
|
+
var urls = settings.urls;
|
|
17
|
+
var filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
18
|
+
var SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
19
|
+
var splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
20
|
+
// Shared authProvider so that the SSE authentication (via `fetchAuth`,
|
|
21
|
+
// used by `pushManager`) and every authenticated HTTP request to the Configs
|
|
22
|
+
// API reuse the same cached credential instead of each fetching their own.
|
|
23
|
+
var authProvider = authProviderFactory(settings, splitHttpClient, telemetryTracker);
|
|
24
|
+
var secureSplitHttpClient = secureSplitHttpClientFactory(splitHttpClient, authProvider);
|
|
25
|
+
var initialAuth = true;
|
|
26
|
+
return {
|
|
27
|
+
fetchAuth: function () {
|
|
28
|
+
// Guard condition to avoid invalidating the cached credential on the first pushManager authentication
|
|
29
|
+
if (initialAuth)
|
|
30
|
+
initialAuth = false;
|
|
31
|
+
else
|
|
32
|
+
authProvider.invalidate();
|
|
33
|
+
return authProvider.credential();
|
|
34
|
+
},
|
|
35
|
+
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
36
|
+
getSdkAPIHealthCheck: function () {
|
|
37
|
+
var url = "".concat(urls.sdk, "/api/version");
|
|
38
|
+
return splitHttpClient(url).then(function () { return true; }).catch(function () { return false; });
|
|
39
|
+
},
|
|
40
|
+
getEventsAPIHealthCheck: function () {
|
|
41
|
+
var url = "".concat(urls.events, "/api/version");
|
|
42
|
+
return splitHttpClient(url).then(function () { return true; }).catch(function () { return false; });
|
|
43
|
+
},
|
|
44
|
+
fetchSplitChanges: function (since, noCache, till, rbSince) {
|
|
45
|
+
var url = "".concat(urls.sdk, "/api/splitChanges?s=").concat(settings.sync.flagSpecVersion, "&since=").concat(since).concat(rbSince ? '&rbSince=' + rbSince : '').concat(filterQueryString || '').concat(till ? '&till=' + till : '');
|
|
46
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
|
|
47
|
+
.catch(function (err) {
|
|
48
|
+
if (err.statusCode === 414)
|
|
49
|
+
settings.log.error(ERROR_TOO_MANY_SETS);
|
|
50
|
+
throw err;
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
fetchSegmentChanges: function (since, segmentName, noCache, till) {
|
|
54
|
+
var url = "".concat(urls.sdk, "/api/segmentChanges/").concat(segmentName, "?since=").concat(since).concat(till ? '&till=' + till : '');
|
|
55
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
|
|
56
|
+
},
|
|
57
|
+
// @TODO support filterQueryString and handle ERROR_TOO_MANY_SETS error
|
|
58
|
+
fetchConfigs: function (since, noCache, till, rbSince) {
|
|
59
|
+
var url = "".concat(urls.configs, "/api/v1/configs?since=").concat(since).concat(rbSince ? '&rbSince=' + rbSince : '').concat(filterQueryString || '').concat(till ? '&till=' + till : '');
|
|
60
|
+
return secureSplitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
|
|
61
|
+
},
|
|
62
|
+
fetchConfigsSegmentChanges: function (since, segmentName, noCache, till) {
|
|
63
|
+
var url = "".concat(urls.configs, "/api/v1/segmentChanges/").concat(segmentName, "?since=").concat(since).concat(till ? '&till=' + till : '');
|
|
64
|
+
return secureSplitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
|
|
65
|
+
},
|
|
66
|
+
fetchMemberships: function (userMatchingKey, noCache, till) {
|
|
67
|
+
/**
|
|
68
|
+
* URI encoding of user keys in order to:
|
|
69
|
+
* - avoid 400 responses (due to URI malformed). E.g.: '/api/memberships/%'
|
|
70
|
+
* - avoid 404 responses. E.g.: '/api/memberships/foo/bar'
|
|
71
|
+
* - match user keys with special characters. E.g.: 'foo%bar', 'foo/bar'
|
|
72
|
+
*/
|
|
73
|
+
var url = "".concat(urls.sdk, "/api/memberships/").concat(encodeURIComponent(userMatchingKey)).concat(till ? '?till=' + till : '');
|
|
74
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(MEMBERSHIPS));
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* Post events.
|
|
78
|
+
*
|
|
79
|
+
* @param body - Events bulk payload
|
|
80
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
81
|
+
*/
|
|
82
|
+
postEventsBulk: function (body, headers) {
|
|
83
|
+
var url = "".concat(urls.events, "/api/events/bulk");
|
|
84
|
+
return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(EVENTS), false, false, false);
|
|
85
|
+
},
|
|
86
|
+
/**
|
|
87
|
+
* Post impressions.
|
|
88
|
+
*
|
|
89
|
+
* @param body - Impressions bulk payload
|
|
90
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
91
|
+
*/
|
|
92
|
+
postTestImpressionsBulk: function (body, headers) {
|
|
93
|
+
var url = "".concat(urls.events, "/api/testImpressions/bulk");
|
|
94
|
+
return secureSplitHttpClient(url, {
|
|
95
|
+
// Adding extra headers to send impressions in OPTIMIZED or DEBUG modes.
|
|
96
|
+
method: 'POST',
|
|
97
|
+
body: body,
|
|
98
|
+
headers: objectAssign({ SplitSDKImpressionsMode: SplitSDKImpressionsMode }, headers)
|
|
99
|
+
}, telemetryTracker.trackHttp(IMPRESSIONS), false, false, false);
|
|
100
|
+
},
|
|
101
|
+
/**
|
|
102
|
+
* Post impressions counts.
|
|
103
|
+
*
|
|
104
|
+
* @param body - Impressions counts payload
|
|
105
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
106
|
+
*/
|
|
107
|
+
postTestImpressionsCount: function (body, headers) {
|
|
108
|
+
var url = "".concat(urls.events, "/api/testImpressions/count");
|
|
109
|
+
return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT), false, false, false);
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Post unique keys for client side.
|
|
113
|
+
*
|
|
114
|
+
* @param body - unique keys payload
|
|
115
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
116
|
+
*/
|
|
117
|
+
postUniqueKeysBulkCs: function (body, headers) {
|
|
118
|
+
var url = "".concat(urls.telemetry, "/api/v1/keys/cs");
|
|
119
|
+
return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY), false, false, false);
|
|
120
|
+
},
|
|
121
|
+
/**
|
|
122
|
+
* Post unique keys for server side.
|
|
123
|
+
*
|
|
124
|
+
* @param body - unique keys payload
|
|
125
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
126
|
+
*/
|
|
127
|
+
postUniqueKeysBulkSs: function (body, headers) {
|
|
128
|
+
var url = "".concat(urls.telemetry, "/api/v1/keys/ss");
|
|
129
|
+
return secureSplitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY), false, false, false);
|
|
130
|
+
},
|
|
131
|
+
postMetricsConfig: function (body, headers) {
|
|
132
|
+
var url = "".concat(urls.telemetry, "/api/v1/metrics/config");
|
|
133
|
+
return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
134
|
+
},
|
|
135
|
+
postMetricsUsage: function (body, headers) {
|
|
136
|
+
var url = "".concat(urls.telemetry, "/api/v1/metrics/usage");
|
|
137
|
+
return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
138
|
+
},
|
|
139
|
+
stop: function () {
|
|
140
|
+
secureSplitHttpClient.stop();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
package/esm/services/splitApi.js
CHANGED
|
@@ -2,12 +2,14 @@ import { splitHttpClientFactory } from './splitHttpClient';
|
|
|
2
2
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
3
3
|
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MEMBERSHIPS } from '../utils/constants';
|
|
4
4
|
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
|
|
5
|
+
import { fetchAuthFactory } from './fetchAuth';
|
|
5
6
|
var noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
|
|
6
7
|
function userKeyToQueryParam(userKey) {
|
|
7
8
|
return 'users=' + encodeURIComponent(userKey); // no need to check availability of `encodeURIComponent`, since it is a global highly supported.
|
|
8
9
|
}
|
|
10
|
+
// @TODO remove when migrating to the new `urls` defaults.
|
|
9
11
|
/**
|
|
10
|
-
* Factory of
|
|
12
|
+
* Factory of ServiceApi objects, which group the collection of Split HTTP endpoints used by the SDK
|
|
11
13
|
*
|
|
12
14
|
* @param settings - validated settings object
|
|
13
15
|
* @param platform - object containing environment-specific dependencies
|
|
@@ -19,6 +21,15 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
19
21
|
var SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
20
22
|
var splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
21
23
|
return {
|
|
24
|
+
fetchAuth: fetchAuthFactory(function (userMatchingKeys) {
|
|
25
|
+
var url = "".concat(urls.auth, "/v2/auth?s=").concat(settings.sync.flagSpecVersion);
|
|
26
|
+
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
27
|
+
var queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
28
|
+
if (queryParams)
|
|
29
|
+
url += '&' + queryParams;
|
|
30
|
+
}
|
|
31
|
+
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
32
|
+
}),
|
|
22
33
|
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
23
34
|
getSdkAPIHealthCheck: function () {
|
|
24
35
|
var url = "".concat(urls.sdk, "/version");
|
|
@@ -28,15 +39,6 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
28
39
|
var url = "".concat(urls.events, "/version");
|
|
29
40
|
return splitHttpClient(url).then(function () { return true; }).catch(function () { return false; });
|
|
30
41
|
},
|
|
31
|
-
fetchAuth: function (userMatchingKeys) {
|
|
32
|
-
var url = "".concat(urls.auth, "/v2/auth?s=").concat(settings.sync.flagSpecVersion);
|
|
33
|
-
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
34
|
-
var queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
35
|
-
if (queryParams)
|
|
36
|
-
url += '&' + queryParams;
|
|
37
|
-
}
|
|
38
|
-
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
39
|
-
},
|
|
40
42
|
fetchSplitChanges: function (since, noCache, till, rbSince) {
|
|
41
43
|
var url = "".concat(urls.sdk, "/splitChanges?s=").concat(settings.sync.flagSpecVersion, "&since=").concat(since).concat(rbSince ? '&rbSince=' + rbSince : '').concat(filterQueryString || '').concat(till ? '&till=' + till : '');
|
|
42
44
|
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
|
|
@@ -122,6 +124,18 @@ export function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
122
124
|
postMetricsUsage: function (body, headers) {
|
|
123
125
|
var url = "".concat(urls.telemetry, "/v1/metrics/usage");
|
|
124
126
|
return splitHttpClient(url, { method: 'POST', body: body, headers: headers }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
127
|
+
},
|
|
128
|
+
// Not used. Just here to satisfy the IServiceApi interface.
|
|
129
|
+
fetchConfigs: function (since, noCache, till, rbSince) {
|
|
130
|
+
var url = "".concat(urls.configs, "/v1/configs?since=").concat(since).concat(rbSince ? '&rbSince=' + rbSince : '').concat(filterQueryString || '').concat(till ? '&till=' + till : '');
|
|
131
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
|
|
132
|
+
},
|
|
133
|
+
fetchConfigsSegmentChanges: function (since, segmentName, noCache, till) {
|
|
134
|
+
var url = "".concat(urls.configs, "/v1/segmentChanges/").concat(segmentName, "?since=").concat(since).concat(till ? '&till=' + till : '');
|
|
135
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
|
|
136
|
+
},
|
|
137
|
+
stop: function () {
|
|
138
|
+
// no-op
|
|
125
139
|
}
|
|
126
140
|
};
|
|
127
141
|
}
|
|
@@ -21,19 +21,17 @@ export function splitHttpClientFactory(settings, _a) {
|
|
|
21
21
|
var commonHeaders = {
|
|
22
22
|
'Accept': 'application/json',
|
|
23
23
|
'Content-Type': 'application/json',
|
|
24
|
-
'Authorization': "Bearer ".concat(authorizationKey)
|
|
25
|
-
'SplitSDKVersion': version
|
|
24
|
+
'Authorization': "Bearer ".concat(authorizationKey)
|
|
26
25
|
};
|
|
27
26
|
if (ip)
|
|
28
27
|
commonHeaders['SplitSDKMachineIP'] = ip;
|
|
29
28
|
if (hostname)
|
|
30
29
|
commonHeaders['SplitSDKMachineName'] = removeNonISO88591(hostname);
|
|
31
|
-
return function httpClient(url, reqOpts, latencyTracker, logErrorsAsInfo) {
|
|
30
|
+
return function httpClient(url, reqOpts, latencyTracker, logErrorsAsInfo, newVersionHeader) {
|
|
32
31
|
if (reqOpts === void 0) { reqOpts = {}; }
|
|
33
32
|
if (latencyTracker === void 0) { latencyTracker = function () { }; }
|
|
34
|
-
if (logErrorsAsInfo === void 0) { logErrorsAsInfo = false; }
|
|
35
33
|
var request = objectAssign({
|
|
36
|
-
headers: decorateHeaders(settings, objectAssign({}, commonHeaders, reqOpts.headers || {})),
|
|
34
|
+
headers: decorateHeaders(settings, objectAssign(newVersionHeader ? { 'X-Harness-FME-SDK-Version': version } : { 'SplitSDKVersion': version }, commonHeaders, reqOpts.headers || {})),
|
|
37
35
|
method: reqOpts.method || 'GET',
|
|
38
36
|
body: reqOpts.body
|
|
39
37
|
}, options);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
|
+
/**
|
|
3
|
+
* This class provides a skeletal implementation of the IDefinitionsCacheAsync interface
|
|
4
|
+
* to minimize the effort required to implement this interface.
|
|
5
|
+
*/
|
|
6
|
+
var AbstractDefinitionsCacheAsync = /** @class */ (function () {
|
|
7
|
+
function AbstractDefinitionsCacheAsync() {
|
|
8
|
+
}
|
|
9
|
+
AbstractDefinitionsCacheAsync.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
10
|
+
var _this = this;
|
|
11
|
+
return Promise.all([
|
|
12
|
+
this.setChangeNumber(changeNumber),
|
|
13
|
+
Promise.all(toAdd.map(function (addedFF) { return _this.add(addedFF); })),
|
|
14
|
+
Promise.all(toRemove.map(function (removedFF) { return _this.remove(removedFF); }))
|
|
15
|
+
]).then(function (_a) {
|
|
16
|
+
var added = _a[1], removed = _a[2];
|
|
17
|
+
return added.some(function (result) { return result; }) || removed.some(function (result) { return result; });
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
// @TODO revisit segment-related methods ('usesSegments', 'getRegisteredSegments', 'registerSegments')
|
|
21
|
+
// noop, just keeping the interface. This is used by standalone client-side API only, and so only implemented by InMemory and InLocalStorage.
|
|
22
|
+
AbstractDefinitionsCacheAsync.prototype.usesSegments = function () {
|
|
23
|
+
return Promise.resolve(true);
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Kill `name` definition and set `defaultTreatment` and `changeNumber`.
|
|
27
|
+
* Used for SPLIT_KILL push notifications.
|
|
28
|
+
*
|
|
29
|
+
* @returns a promise that is resolved once the definition kill operation is performed. The fulfillment value is a boolean: `true` if the operation succeeded updating the definition or `false` if no definition is updated,
|
|
30
|
+
* for instance, if the `changeNumber` is old, or if the definition is not found (e.g., `/splitchanges` hasn't been fetched yet), or if the storage fails to apply the update.
|
|
31
|
+
* The promise will never be rejected.
|
|
32
|
+
*/
|
|
33
|
+
AbstractDefinitionsCacheAsync.prototype.killLocally = function (name, defaultTreatment, changeNumber) {
|
|
34
|
+
var _this = this;
|
|
35
|
+
return this.get(name).then(function (definition) {
|
|
36
|
+
if (definition && (!definition.changeNumber || definition.changeNumber < changeNumber)) {
|
|
37
|
+
var newDefinition = objectAssign({}, definition);
|
|
38
|
+
newDefinition.killed = true;
|
|
39
|
+
newDefinition.defaultTreatment = defaultTreatment;
|
|
40
|
+
newDefinition.changeNumber = changeNumber;
|
|
41
|
+
return _this.add(newDefinition);
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}).catch(function () { return false; });
|
|
45
|
+
};
|
|
46
|
+
return AbstractDefinitionsCacheAsync;
|
|
47
|
+
}());
|
|
48
|
+
export { AbstractDefinitionsCacheAsync };
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
|
+
import { IN_SEGMENT, IN_LARGE_SEGMENT } from '../utils/constants';
|
|
3
|
+
/**
|
|
4
|
+
* This class provides a skeletal implementation of the IDefinitionsCacheSync interface
|
|
5
|
+
* to minimize the effort required to implement this interface.
|
|
6
|
+
*/
|
|
7
|
+
var AbstractDefinitionsCacheSync = /** @class */ (function () {
|
|
8
|
+
function AbstractDefinitionsCacheSync() {
|
|
9
|
+
}
|
|
10
|
+
AbstractDefinitionsCacheSync.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
11
|
+
var _this = this;
|
|
12
|
+
var updated = toAdd.map(function (addedFF) { return _this.add(addedFF); }).some(function (result) { return result; });
|
|
13
|
+
updated = toRemove.map(function (removedFF) { return _this.remove(removedFF); }).some(function (result) { return result; }) || updated;
|
|
14
|
+
this.setChangeNumber(changeNumber);
|
|
15
|
+
return updated;
|
|
16
|
+
};
|
|
17
|
+
AbstractDefinitionsCacheSync.prototype.getMany = function (names) {
|
|
18
|
+
var _this = this;
|
|
19
|
+
var definitions = {};
|
|
20
|
+
names.forEach(function (name) {
|
|
21
|
+
definitions[name] = _this.get(name);
|
|
22
|
+
});
|
|
23
|
+
return definitions;
|
|
24
|
+
};
|
|
25
|
+
AbstractDefinitionsCacheSync.prototype.getAll = function () {
|
|
26
|
+
var _this = this;
|
|
27
|
+
return this.getNames().map(function (key) { return _this.get(key); });
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Kill `name` definition and set `defaultTreatment` and `changeNumber`.
|
|
31
|
+
* Used for SPLIT_KILL push notifications.
|
|
32
|
+
*
|
|
33
|
+
* @returns `true` if the operation successed updating the definition, or `false` if no definition is updated,
|
|
34
|
+
* for instance, if the `changeNumber` is old, or if the definition is not found (e.g., `/splitchanges` hasn't been fetched yet), or if the storage fails to apply the update.
|
|
35
|
+
*/
|
|
36
|
+
AbstractDefinitionsCacheSync.prototype.killLocally = function (name, defaultTreatment, changeNumber) {
|
|
37
|
+
var definition = this.get(name);
|
|
38
|
+
if (definition && (!definition.changeNumber || definition.changeNumber < changeNumber)) {
|
|
39
|
+
var newDefinition = objectAssign({}, definition);
|
|
40
|
+
newDefinition.killed = true;
|
|
41
|
+
newDefinition.defaultTreatment = defaultTreatment;
|
|
42
|
+
newDefinition.changeNumber = changeNumber;
|
|
43
|
+
return this.add(newDefinition);
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
};
|
|
47
|
+
return AbstractDefinitionsCacheSync;
|
|
48
|
+
}());
|
|
49
|
+
export { AbstractDefinitionsCacheSync };
|
|
50
|
+
/**
|
|
51
|
+
* Given a parsed definition, it returns a boolean flagging if its conditions use segments matchers (rules & whitelists).
|
|
52
|
+
* This util is intended to simplify the implementation of `definitionsCache::usesSegments` method
|
|
53
|
+
*/
|
|
54
|
+
export function usesSegments(ruleEntity) {
|
|
55
|
+
var conditions = ruleEntity.conditions || [];
|
|
56
|
+
for (var i = 0; i < conditions.length; i++) {
|
|
57
|
+
var matchers = conditions[i].matcherGroup.matchers;
|
|
58
|
+
for (var j = 0; j < matchers.length; j++) {
|
|
59
|
+
var matcher = matchers[j].matcherType;
|
|
60
|
+
if (matcher === IN_SEGMENT || matcher === IN_LARGE_SEGMENT)
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
var excluded = ruleEntity.excluded;
|
|
65
|
+
if (excluded && excluded.segments && excluded.segments.length > 0)
|
|
66
|
+
return true;
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
export function usesSegmentsSync(storage) {
|
|
70
|
+
return storage.definitions.usesSegments() || storage.rbSegments.usesSegments();
|
|
71
|
+
}
|