@splitsoftware/splitio-commons 3.1.1-rc.1 → 3.1.1-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +10 -1
- package/cjs/evaluator/condition/index.js +2 -2
- package/cjs/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +8 -5
- package/cjs/evaluator/index.js +71 -76
- package/cjs/listeners/browser.js +1 -2
- package/cjs/listeners/node.js +4 -4
- package/cjs/logger/constants.js +20 -15
- package/cjs/logger/messages/debug.js +6 -5
- package/cjs/logger/messages/error.js +3 -3
- package/cjs/logger/messages/info.js +4 -3
- package/cjs/logger/messages/warn.js +7 -6
- package/cjs/presets/serverSide.js +2 -2
- package/cjs/readiness/constants.js +4 -5
- package/cjs/readiness/readinessManager.js +23 -23
- package/cjs/readiness/sdkReadinessManager.js +17 -1
- package/cjs/sdkClient/client.js +51 -27
- package/cjs/sdkClient/clientInputValidation.js +17 -6
- package/cjs/sdkClient/sdkClient.js +60 -3
- package/cjs/sdkFactory/index.js +15 -11
- package/cjs/sdkManager/index.js +5 -5
- package/cjs/services/decorateHeaders.js +1 -2
- package/cjs/services/splitApi.js +1 -14
- package/cjs/services/splitHttpClient.js +5 -3
- package/cjs/storages/AbstractSplitsCacheAsync.js +51 -0
- package/cjs/storages/AbstractSplitsCacheSync.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/MySegmentsCacheInLocal.js +2 -1
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +10 -10
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +194 -0
- package/cjs/storages/inLocalStorage/index.js +6 -6
- package/cjs/storages/inLocalStorage/validateCache.js +3 -3
- package/cjs/storages/inMemory/InMemoryStorage.js +4 -4
- package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -5
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +5 -5
- package/cjs/storages/inMemory/SegmentsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +121 -0
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +5 -5
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/RedisAdapter.js +8 -5
- package/cjs/storages/inRedis/{DefinitionsCacheInRedis.js → SplitsCacheInRedis.js} +77 -77
- package/cjs/storages/inRedis/index.js +2 -2
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +1 -1
- package/cjs/storages/pluggable/SplitsCachePluggable.js +212 -0
- package/cjs/storages/pluggable/index.js +2 -2
- package/cjs/storages/setRolloutPlan.js +4 -4
- package/cjs/storages/utils.js +0 -1
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +5 -5
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +15 -24
- package/cjs/sync/polling/pollingManagerCS.js +20 -20
- package/cjs/sync/polling/pollingManagerSS.js +15 -15
- package/cjs/sync/polling/syncTasks/segmentsSyncTask.js +3 -2
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +13 -0
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +3 -3
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +6 -6
- package/cjs/sync/polling/updaters/{definitionChangesUpdater.js → splitChangesUpdater.js} +68 -65
- package/cjs/sync/streaming/AuthClient/index.js +1 -1
- package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +1 -1
- package/cjs/sync/streaming/SSEHandler/index.js +1 -1
- package/cjs/sync/streaming/UpdateWorkers/{DefinitionsUpdateWorker.js → SplitsUpdateWorker.js} +19 -16
- package/cjs/sync/streaming/constants.js +3 -3
- package/cjs/sync/streaming/parseUtils.js +11 -11
- package/cjs/sync/streaming/pushManager.js +16 -16
- package/cjs/sync/submitters/eventsSubmitter.js +1 -1
- package/cjs/sync/submitters/impressionCountsSubmitter.js +1 -1
- package/cjs/sync/submitters/impressionsSubmitter.js +4 -5
- package/cjs/sync/submitters/telemetrySubmitter.js +4 -3
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +1 -1
- package/cjs/sync/syncManagerOnline.js +7 -12
- package/cjs/utils/Backoff.js +0 -20
- package/cjs/utils/inputValidation/index.js +7 -7
- package/cjs/utils/inputValidation/isOperational.js +4 -4
- package/cjs/utils/inputValidation/{definition.js → split.js} +10 -10
- package/cjs/utils/inputValidation/{definitionExistence.js → splitExistence.js} +6 -6
- package/cjs/utils/inputValidation/splits.js +25 -0
- package/cjs/utils/labels/index.js +3 -3
- package/cjs/utils/settingsValidation/index.js +0 -2
- package/cjs/utils/settingsValidation/splitFilters.js +3 -3
- package/cjs/utils/settingsValidation/url.js +2 -6
- package/esm/evaluator/condition/index.js +2 -2
- package/esm/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +8 -5
- package/esm/evaluator/index.js +71 -75
- package/esm/listeners/browser.js +1 -2
- package/esm/listeners/node.js +4 -4
- package/esm/logger/constants.js +17 -12
- package/esm/logger/messages/debug.js +6 -5
- package/esm/logger/messages/error.js +3 -3
- package/esm/logger/messages/info.js +4 -3
- package/esm/logger/messages/warn.js +7 -6
- package/esm/presets/serverSide.js +2 -2
- package/esm/readiness/constants.js +3 -4
- package/esm/readiness/readinessManager.js +24 -24
- package/esm/readiness/sdkReadinessManager.js +18 -2
- package/esm/sdkClient/client.js +51 -26
- package/esm/sdkClient/clientInputValidation.js +19 -8
- package/esm/sdkClient/sdkClient.js +60 -3
- package/esm/sdkFactory/index.js +16 -12
- package/esm/sdkManager/index.js +6 -6
- package/esm/services/decorateHeaders.js +1 -2
- package/esm/services/splitApi.js +1 -14
- package/esm/services/splitHttpClient.js +5 -3
- package/esm/storages/AbstractSplitsCacheAsync.js +48 -0
- package/esm/storages/AbstractSplitsCacheSync.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/MySegmentsCacheInLocal.js +2 -1
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +7 -7
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +191 -0
- package/esm/storages/inLocalStorage/index.js +6 -6
- package/esm/storages/inLocalStorage/validateCache.js +3 -3
- package/esm/storages/inMemory/InMemoryStorage.js +4 -4
- package/esm/storages/inMemory/InMemoryStorageCS.js +5 -5
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +2 -2
- package/esm/storages/inMemory/SegmentsCacheInMemory.js +1 -1
- package/esm/storages/inMemory/SplitsCacheInMemory.js +118 -0
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +5 -5
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/RedisAdapter.js +8 -5
- package/esm/storages/inRedis/{DefinitionsCacheInRedis.js → SplitsCacheInRedis.js} +76 -76
- package/esm/storages/inRedis/index.js +2 -2
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +1 -1
- package/esm/storages/pluggable/SplitsCachePluggable.js +209 -0
- package/esm/storages/pluggable/index.js +2 -2
- package/esm/storages/setRolloutPlan.js +4 -4
- package/esm/storages/utils.js +0 -1
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +6 -6
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +16 -25
- package/esm/sync/polling/pollingManagerCS.js +17 -17
- package/esm/sync/polling/pollingManagerSS.js +15 -15
- package/esm/sync/polling/syncTasks/segmentsSyncTask.js +3 -2
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +9 -0
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -3
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +7 -7
- package/esm/sync/polling/updaters/{definitionChangesUpdater.js → splitChangesUpdater.js} +68 -65
- package/esm/sync/streaming/AuthClient/index.js +1 -1
- package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +2 -2
- package/esm/sync/streaming/SSEHandler/index.js +2 -2
- package/esm/sync/streaming/UpdateWorkers/{DefinitionsUpdateWorker.js → SplitsUpdateWorker.js} +18 -15
- package/esm/sync/streaming/constants.js +2 -2
- package/esm/sync/streaming/parseUtils.js +9 -9
- package/esm/sync/streaming/pushManager.js +18 -18
- package/esm/sync/submitters/eventsSubmitter.js +1 -1
- package/esm/sync/submitters/impressionCountsSubmitter.js +1 -1
- package/esm/sync/submitters/impressionsSubmitter.js +4 -5
- package/esm/sync/submitters/telemetrySubmitter.js +4 -3
- package/esm/sync/submitters/uniqueKeysSubmitter.js +1 -1
- package/esm/sync/syncManagerOnline.js +6 -11
- package/esm/utils/Backoff.js +0 -20
- package/esm/utils/inputValidation/index.js +3 -3
- package/esm/utils/inputValidation/isOperational.js +4 -4
- package/esm/utils/inputValidation/{definition.js → split.js} +8 -8
- package/esm/utils/inputValidation/splitExistence.js +15 -0
- package/esm/utils/inputValidation/splits.js +21 -0
- package/esm/utils/labels/index.js +2 -2
- package/esm/utils/settingsValidation/index.js +0 -2
- package/esm/utils/settingsValidation/splitFilters.js +3 -3
- package/esm/utils/settingsValidation/url.js +2 -6
- package/package.json +6 -7
- package/src/dtos/types.ts +1 -1
- package/src/evaluator/index.ts +11 -5
- package/src/evaluator/types.ts +1 -0
- package/src/presets/serverSide.ts +3 -3
- package/src/sdkClient/client.ts +3 -4
- package/src/sdkClient/sdkLifecycle.ts +3 -0
- package/src/services/authProvider.ts +41 -19
- package/src/services/secureSplitHttpClient.ts +10 -18
- package/src/services/serviceApi.ts +27 -24
- package/src/services/splitApi.ts +10 -9
- package/src/services/types.ts +5 -4
- package/src/storages/inRedis/RedisAdapter.ts +8 -5
- package/src/sync/polling/pollingManagerCS.ts +80 -78
- package/src/sync/polling/pollingManagerSS.ts +49 -45
- package/src/sync/polling/updaters/definitionChangesUpdater.ts +3 -3
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
- package/src/sync/streaming/AuthClient/index.ts +14 -15
- package/src/sync/streaming/AuthClient/types.ts +9 -13
- package/src/sync/streaming/SSEClient/index.ts +2 -2
- package/src/sync/streaming/SSEClient/types.ts +2 -2
- package/src/sync/streaming/SSEHandler/index.ts +2 -1
- package/src/sync/streaming/SSEHandler/types.ts +2 -2
- package/src/sync/streaming/constants.ts +1 -0
- package/src/sync/streaming/pushManager.ts +32 -42
- package/src/sync/streaming/types.ts +3 -2
- package/src/sync/submitters/impressionsSubmitter.ts +3 -3
- package/src/sync/submitters/types.ts +2 -2
- package/src/sync/syncManagerOnline.ts +2 -8
- package/src/utils/Backoff.ts +16 -4
- package/types/splitio.d.ts +5 -4
- package/cjs/platform/browser.js +0 -15
- package/cjs/platform/getEventSource/browser.js +0 -8
- package/cjs/platform/getEventSource/eventsource.js +0 -480
- package/cjs/platform/getEventSource/node.js +0 -26
- package/cjs/platform/getFetch/browser.js +0 -9
- package/cjs/platform/getFetch/node.js +0 -27
- package/cjs/platform/getOptions/node.js +0 -25
- package/cjs/platform/node.js +0 -17
- package/cjs/sdkClient/sdkLifecycle.js +0 -72
- package/cjs/sdkClient/trackMethod.js +0 -56
- package/cjs/services/authProvider.js +0 -67
- package/cjs/services/secureSplitHttpClient.js +0 -39
- package/cjs/services/serviceApi.js +0 -145
- package/cjs/storages/AbstractDefinitionsCacheAsync.js +0 -51
- package/cjs/storages/AbstractDefinitionsCacheSync.js +0 -76
- package/cjs/storages/inLocalStorage/DefinitionsCacheInLocal.js +0 -194
- package/cjs/storages/inMemory/DefinitionsCacheInMemory.js +0 -121
- package/cjs/storages/pluggable/DefinitionsCachePluggable.js +0 -212
- package/cjs/sync/polling/syncTasks/definitionsSyncTask.js +0 -13
- package/cjs/utils/EventEmitter.js +0 -391
- package/cjs/utils/filter/bloomFilter.js +0 -37
- package/cjs/utils/inputValidation/definitions.js +0 -25
- package/cjs/utils/inputValidation/target.js +0 -22
- package/esm/platform/browser.js +0 -12
- package/esm/platform/getEventSource/browser.js +0 -4
- package/esm/platform/getEventSource/eventsource.js +0 -480
- package/esm/platform/getEventSource/node.js +0 -20
- package/esm/platform/getFetch/browser.js +0 -5
- package/esm/platform/getFetch/node.js +0 -22
- package/esm/platform/getOptions/node.js +0 -20
- package/esm/platform/node.js +0 -14
- package/esm/sdkClient/sdkLifecycle.js +0 -68
- package/esm/sdkClient/trackMethod.js +0 -52
- package/esm/services/authProvider.js +0 -63
- package/esm/services/secureSplitHttpClient.js +0 -35
- package/esm/services/serviceApi.js +0 -141
- package/esm/storages/AbstractDefinitionsCacheAsync.js +0 -48
- package/esm/storages/AbstractDefinitionsCacheSync.js +0 -71
- package/esm/storages/inLocalStorage/DefinitionsCacheInLocal.js +0 -191
- package/esm/storages/inMemory/DefinitionsCacheInMemory.js +0 -118
- package/esm/storages/pluggable/DefinitionsCachePluggable.js +0 -209
- package/esm/sync/polling/syncTasks/definitionsSyncTask.js +0 -9
- package/esm/utils/EventEmitter.js +0 -387
- package/esm/utils/filter/bloomFilter.js +0 -33
- package/esm/utils/inputValidation/definitionExistence.js +0 -15
- package/esm/utils/inputValidation/definitions.js +0 -21
- package/esm/utils/inputValidation/target.js +0 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "3.1.1-rc.
|
|
3
|
+
"version": "3.1.1-rc.10",
|
|
4
4
|
"description": "Split JavaScript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"check:lint": "eslint src types --ext .js,.ts",
|
|
21
21
|
"check:types": "tsc --noEmit",
|
|
22
22
|
"build": "npm run build:cjs && npm run build:esm",
|
|
23
|
-
"build:esm": "rimraf esm && tsc -m es2015 --outDir esm",
|
|
24
|
-
"build:cjs": "rimraf cjs && tsc -m CommonJS --outDir cjs",
|
|
23
|
+
"build:esm": "rimraf esm && tsc -p tsconfig.build.json -m es2015 --outDir esm",
|
|
24
|
+
"build:cjs": "rimraf cjs && tsc -p tsconfig.build.json -m CommonJS --outDir cjs",
|
|
25
25
|
"test": "jest",
|
|
26
26
|
"test:coverage": "jest --coverage",
|
|
27
27
|
"all": "npm run check && npm run build && npm run test",
|
|
@@ -45,12 +45,12 @@
|
|
|
45
45
|
"bugs": "https://github.com/splitio/javascript-commons/issues",
|
|
46
46
|
"homepage": "https://github.com/splitio/javascript-commons#readme",
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@types/
|
|
48
|
+
"@types/node": ">=14.0.0",
|
|
49
49
|
"tslib": "^2.3.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"bloom-filters": "^3.0.0",
|
|
53
|
-
"ioredis": "^4.28.0 || ^5.0.0",
|
|
53
|
+
"ioredis": "^4.28.0 || ^5.0.0 || ^6.0.0",
|
|
54
54
|
"node-fetch": "^2.7.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependenciesMeta": {
|
|
@@ -65,7 +65,6 @@
|
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@types/ioredis": "^4.28.0",
|
|
69
68
|
"@types/jest": "^27.0.0",
|
|
70
69
|
"@types/lodash": "^4.14.162",
|
|
71
70
|
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
@@ -77,7 +76,7 @@
|
|
|
77
76
|
"eslint-plugin-import": "^2.25.3",
|
|
78
77
|
"eslint-plugin-tsdoc": "^0.3.0",
|
|
79
78
|
"fetch-mock": "^9.11.0",
|
|
80
|
-
"ioredis": "^
|
|
79
|
+
"ioredis": "^6.0.0",
|
|
81
80
|
"jest": "^27.2.3",
|
|
82
81
|
"jest-localstorage-mock": "^2.4.3",
|
|
83
82
|
"lodash": "^4.17.21",
|
package/src/dtos/types.ts
CHANGED
|
@@ -236,7 +236,7 @@ export interface IDefinition extends TargetingEntity {
|
|
|
236
236
|
} | null;
|
|
237
237
|
/** Definition classification. Absent means a feature flag. */
|
|
238
238
|
type?: ConfigType;
|
|
239
|
-
/** Only meaningful when `type === '
|
|
239
|
+
/** Only meaningful when `type === 'ai'`. */
|
|
240
240
|
subtype?: ConfigSubtype;
|
|
241
241
|
}
|
|
242
242
|
|
package/src/evaluator/index.ts
CHANGED
|
@@ -31,6 +31,13 @@ function treatmentsException(definitionNames: string[]) {
|
|
|
31
31
|
return evaluations;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function setEvaluationDataFromDefinition(evaluation: IEvaluation, definition: IDefinition): IEvaluation {
|
|
35
|
+
return objectAssign(evaluation, {
|
|
36
|
+
definition,
|
|
37
|
+
config: definition.configurations && definition.configurations[evaluation.treatment] || null
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
34
41
|
export function evaluateFeature(
|
|
35
42
|
log: ILogger,
|
|
36
43
|
key: SplitIO.SplitKey,
|
|
@@ -137,8 +144,8 @@ function getEvaluation(
|
|
|
137
144
|
const evaluation = split.getTreatment(key, attributes, evaluateFeature);
|
|
138
145
|
|
|
139
146
|
return thenable(evaluation) ?
|
|
140
|
-
evaluation.then(result =>
|
|
141
|
-
|
|
147
|
+
evaluation.then(result => setEvaluationDataFromDefinition(result, definition)) :
|
|
148
|
+
setEvaluationDataFromDefinition(evaluation, definition);
|
|
142
149
|
}
|
|
143
150
|
|
|
144
151
|
return EVALUATION_DEFINITION_NOT_FOUND;
|
|
@@ -187,11 +194,10 @@ function getDefaultTreatment(
|
|
|
187
194
|
definition: IDefinition | null,
|
|
188
195
|
): MaybeThenable<IEvaluation> {
|
|
189
196
|
if (definition) {
|
|
190
|
-
return {
|
|
197
|
+
return setEvaluationDataFromDefinition({
|
|
191
198
|
treatment: definition.defaultTreatment,
|
|
192
199
|
label: NO_CONDITION_MATCH, // "default rule"
|
|
193
|
-
|
|
194
|
-
};
|
|
200
|
+
}, definition);
|
|
195
201
|
}
|
|
196
202
|
|
|
197
203
|
return EVALUATION_DEFINITION_NOT_FOUND;
|
package/src/evaluator/types.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface IEvaluation {
|
|
|
22
22
|
treatment: string,
|
|
23
23
|
label: string,
|
|
24
24
|
definition?: IDefinition
|
|
25
|
+
config?: string | null | SplitIO.JsonObject
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export type IDefinitionEvaluator = (log: ILogger, key: SplitIO.SplitKey, definitionName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { splitApiFactory } from '../services/splitApi';
|
|
2
2
|
import { syncManagerOnlineFactory } from '../sync/syncManagerOnline';
|
|
3
3
|
import { pushManagerFactory } from '../sync/streaming/pushManager';
|
|
4
4
|
import { pollingManagerSSFactory } from '../sync/polling/pollingManagerSS';
|
|
@@ -7,11 +7,11 @@ import { sdkManagerFactory } from '../sdkManager';
|
|
|
7
7
|
import { sdkClientMethodFactory } from '../sdkClient/sdkClientMethod';
|
|
8
8
|
import { impressionObserverSSFactory } from '../trackers/impressionObserver/impressionObserverSS';
|
|
9
9
|
|
|
10
|
-
const syncManagerOnlineSSFactory = syncManagerOnlineFactory(pollingManagerSSFactory, pushManagerFactory);
|
|
10
|
+
const syncManagerOnlineSSFactory = syncManagerOnlineFactory(pollingManagerSSFactory(), pushManagerFactory);
|
|
11
11
|
|
|
12
12
|
export const serverSideModules = {
|
|
13
13
|
storageFactory: InMemoryStorageFactory,
|
|
14
|
-
serviceApiFactory,
|
|
14
|
+
serviceApiFactory: splitApiFactory,
|
|
15
15
|
syncManagerFactory: syncManagerOnlineSSFactory,
|
|
16
16
|
sdkManagerFactory,
|
|
17
17
|
sdkClientMethodFactory,
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -142,8 +142,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
142
142
|
const bucketingKey = getBucketing(key);
|
|
143
143
|
|
|
144
144
|
const { definition } = evaluation;
|
|
145
|
-
let { treatment, label } = evaluation;
|
|
146
|
-
let config = definition?.configurations?.[treatment] || null;
|
|
145
|
+
let { treatment, label, config = null } = evaluation;
|
|
147
146
|
|
|
148
147
|
if (treatment === CONTROL) {
|
|
149
148
|
const fallbackTreatment = fallbackCalculator(featureFlagName, label);
|
|
@@ -162,10 +161,10 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
162
161
|
time: Date.now(),
|
|
163
162
|
bucketingKey,
|
|
164
163
|
label,
|
|
165
|
-
changeNumber: definition
|
|
164
|
+
changeNumber: definition?.changeNumber,
|
|
166
165
|
properties: stringify(options.properties)
|
|
167
166
|
}, // @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
|
|
168
|
-
disabled: options.impressionsDisabled || definition
|
|
167
|
+
disabled: options.impressionsDisabled || definition?.impressionsDisabled
|
|
169
168
|
});
|
|
170
169
|
}
|
|
171
170
|
|
|
@@ -69,6 +69,9 @@ export function sdkLifecycleFactory(params: ISdkFactoryContext, isSharedClient?:
|
|
|
69
69
|
|
|
70
70
|
// Stop background jobs
|
|
71
71
|
syncManager && syncManager.stop();
|
|
72
|
+
|
|
73
|
+
// Stop the service API (the auth provider backoff retries specifically) before flushing,
|
|
74
|
+
// so that a pending/never-resolved auth retry doesn't block the flush forever
|
|
72
75
|
serviceApi && serviceApi.stop();
|
|
73
76
|
|
|
74
77
|
return __flush().then(() => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ISplitHttpClient, NetworkError } from './types';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { IJwtCredential } from '../sync/streaming/AuthClient/types';
|
|
3
|
+
import { fetchAuthFactory } from '../sync/streaming/AuthClient';
|
|
4
4
|
import { Backoff } from '../utils/Backoff';
|
|
5
5
|
import { LOG_PREFIX_SYNC_AUTH } from '../logger/constants';
|
|
6
6
|
import { ISettings } from '../types';
|
|
@@ -9,12 +9,24 @@ import { ITelemetryTracker } from '../trackers/types';
|
|
|
9
9
|
|
|
10
10
|
const SKEW_SECONDS = 30;
|
|
11
11
|
|
|
12
|
-
function isExpired(credential:
|
|
12
|
+
function isExpired(credential: IJwtCredential): boolean {
|
|
13
13
|
return Date.now() / 1000 + SKEW_SECONDS >= credential.decodedToken.exp;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function noCachedCredentialError() {
|
|
17
|
+
return new Error(LOG_PREFIX_SYNC_AUTH + 'stopped without a cached credential');
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
export interface IAuthProvider {
|
|
17
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Returns the cached credential, or fetches a new one if there isn't one cached or it's expired,
|
|
23
|
+
* retrying with backoff on recoverable errors. Used by `secureSplitHttpClient` and `serviceApi.fetchAuth`.
|
|
24
|
+
*/
|
|
25
|
+
credential(): Promise<IJwtCredential>;
|
|
26
|
+
/**
|
|
27
|
+
* Invalidates/clears the cached credential. Used by `secureSplitHttpClient` in the special case of 401 error,
|
|
28
|
+
* and by `serviceApi.fetchAuth` to force a credential/token refresh.
|
|
29
|
+
*/
|
|
18
30
|
invalidate(): void;
|
|
19
31
|
stop(): void;
|
|
20
32
|
}
|
|
@@ -27,28 +39,27 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
|
|
|
27
39
|
|
|
28
40
|
const { urls, log } = settings;
|
|
29
41
|
|
|
30
|
-
|
|
42
|
+
const fetchAuth = fetchAuthFactory(() => {
|
|
31
43
|
let url = `${urls.auth}/api/v3/auth?capabilities=config,aiconfig`;
|
|
32
44
|
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN), false, true);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const authenticate = authenticateFactory(fetchAuth);
|
|
45
|
+
});
|
|
36
46
|
const backoff = new Backoff(fetchCredential);
|
|
37
47
|
|
|
38
|
-
let cachedCredential:
|
|
39
|
-
let inFlightPromise: Promise<
|
|
48
|
+
let cachedCredential: IJwtCredential | undefined;
|
|
49
|
+
let inFlightPromise: Promise<IJwtCredential> | undefined;
|
|
40
50
|
let stopped = false;
|
|
41
51
|
|
|
42
|
-
function fetchCredential(): Promise<
|
|
43
|
-
return
|
|
52
|
+
function fetchCredential(): Promise<IJwtCredential> {
|
|
53
|
+
return fetchAuth().then((credential: IJwtCredential) => {
|
|
44
54
|
log.info(LOG_PREFIX_SYNC_AUTH + 'credential fetched successfully');
|
|
45
55
|
cachedCredential = credential;
|
|
46
56
|
inFlightPromise = undefined;
|
|
47
57
|
backoff.reset();
|
|
48
58
|
return credential;
|
|
49
59
|
}).catch((error: NetworkError) => {
|
|
50
|
-
// Avoid
|
|
51
|
-
|
|
60
|
+
// Avoid unnecessary retries after stop(): fall back to any cached credential, or reject
|
|
61
|
+
// (never resolve with `undefined`, which callers would otherwise send as an unauthenticated request).
|
|
62
|
+
if (stopped) return cachedCredential || Promise.reject(noCachedCredentialError());
|
|
52
63
|
|
|
53
64
|
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
|
|
54
65
|
log.error(LOG_PREFIX_SYNC_AUTH + 'non-retryable error fetching credential (status ' + error.statusCode + '): ' + error.message);
|
|
@@ -62,14 +73,24 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
|
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
return {
|
|
65
|
-
credential(): Promise<
|
|
76
|
+
credential(): Promise<IJwtCredential> {
|
|
66
77
|
if (cachedCredential && !isExpired(cachedCredential)) {
|
|
67
78
|
return Promise.resolve(cachedCredential);
|
|
68
79
|
}
|
|
69
80
|
|
|
81
|
+
// Reuse any fetch already in flight (e.g. one started just before stop()), instead of firing a redundant one.
|
|
82
|
+
if (inFlightPromise) return inFlightPromise;
|
|
83
|
+
|
|
84
|
+
// No cached credential to fall back to and stopped: reject so callers don't send an unauthenticated request.
|
|
85
|
+
if (stopped && !cachedCredential) {
|
|
86
|
+
return Promise.reject(noCachedCredentialError());
|
|
87
|
+
}
|
|
88
|
+
|
|
70
89
|
if (cachedCredential) log.debug(LOG_PREFIX_SYNC_AUTH + 'cached credential expired');
|
|
71
90
|
|
|
72
|
-
|
|
91
|
+
// If stopped, this is a last, bounded refetch: fetchCredential()'s catch short-circuits (no backoff retry)
|
|
92
|
+
// once stopped, so it's a single attempt that falls back to the stale cached credential on failure.
|
|
93
|
+
return inFlightPromise = fetchCredential();
|
|
73
94
|
},
|
|
74
95
|
|
|
75
96
|
invalidate() {
|
|
@@ -78,9 +99,10 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
|
|
|
78
99
|
|
|
79
100
|
stop() {
|
|
80
101
|
stopped = true;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
backoff.
|
|
102
|
+
// Keep any in-flight fetch and already-cached credential usable (e.g. by a final flush on destroy).
|
|
103
|
+
// If a retry is only pending (not actively in flight), settle it immediately instead of waiting out the
|
|
104
|
+
// backoff delay: with the cached credential if there is one, or rejected otherwise.
|
|
105
|
+
backoff.reset(cachedCredential ? { value: cachedCredential } : { error: noCachedCredentialError() });
|
|
84
106
|
}
|
|
85
107
|
};
|
|
86
108
|
}
|
|
@@ -1,36 +1,28 @@
|
|
|
1
|
-
import { IRequestOptions, IResponse, ISecureSplitHttpClient, NetworkError } from './types';
|
|
2
|
-
import {
|
|
3
|
-
import { IPlatform } from '../sdkFactory/types';
|
|
4
|
-
import { splitHttpClientFactory } from './splitHttpClient';
|
|
5
|
-
import { authProviderFactory } from './authProvider';
|
|
6
|
-
import { ITelemetryTracker } from '../trackers/types';
|
|
1
|
+
import { IRequestOptions, IResponse, ISecureSplitHttpClient, ISplitHttpClient, NetworkError } from './types';
|
|
2
|
+
import { IAuthProvider } from './authProvider';
|
|
7
3
|
|
|
8
4
|
/**
|
|
9
5
|
* Factory of Secure HTTP client, which authenticates requests using a JWT token.
|
|
10
6
|
* On 401 responses, invalidates the cached credential and retries once with a fresh token.
|
|
11
7
|
*
|
|
12
|
-
* @param
|
|
13
|
-
* @param
|
|
14
|
-
* @param fetchAuth - function to fetch auth credentials from the /v2/auth endpoint
|
|
8
|
+
* @param splitHttpClient - `splitHttpClientFactory` to use for making requests
|
|
9
|
+
* @param authProvider - `authProviderFactory` to use for authentication
|
|
15
10
|
*/
|
|
16
|
-
export function secureSplitHttpClientFactory(
|
|
11
|
+
export function secureSplitHttpClientFactory(splitHttpClient: ISplitHttpClient, authProvider: IAuthProvider): ISecureSplitHttpClient {
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
function makeRequest(url: string, options: IRequestOptions | undefined, latencyTracker: ((error?: NetworkError) => void) | undefined, logErrorsAsInfo: boolean | undefined, token: string): Promise<IResponse> {
|
|
22
|
-
return splitHttpClient(url, { ...options, headers: { ...options?.headers, Authorization: `Bearer ${token}` } }, latencyTracker, logErrorsAsInfo, true);
|
|
13
|
+
function makeRequest(url: string, options: IRequestOptions | undefined, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader?: boolean, token?: string): Promise<IResponse> {
|
|
14
|
+
return splitHttpClient(url, token ? { ...options, headers: { ...options?.headers, Authorization: `Bearer ${token}` } } : options, latencyTracker, logErrorsAsInfo, newVersionHeader);
|
|
23
15
|
}
|
|
24
16
|
|
|
25
|
-
const httpClient = function (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean): Promise<IResponse> {
|
|
17
|
+
const httpClient = function (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader = true, useJwt = true): Promise<IResponse> {
|
|
26
18
|
return authProvider.credential().then(credential => {
|
|
27
|
-
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, credential.token)
|
|
19
|
+
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? credential.token : undefined)
|
|
28
20
|
.catch((error: NetworkError) => {
|
|
29
21
|
if (error.statusCode === 401) {
|
|
30
22
|
// retry once for 401, in case the token has just expired
|
|
31
23
|
authProvider.invalidate();
|
|
32
24
|
return authProvider.credential().then(newCredential => {
|
|
33
|
-
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newCredential.token);
|
|
25
|
+
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? newCredential.token : undefined);
|
|
34
26
|
});
|
|
35
27
|
}
|
|
36
28
|
throw error;
|
|
@@ -1,40 +1,52 @@
|
|
|
1
1
|
import { IPlatform } from '../sdkFactory/types';
|
|
2
2
|
import { ISettings } from '../types';
|
|
3
3
|
import { splitHttpClientFactory } from './splitHttpClient';
|
|
4
|
-
import {
|
|
4
|
+
import { IServiceApi } from './types';
|
|
5
5
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
6
6
|
import { ITelemetryTracker } from '../trackers/types';
|
|
7
|
-
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY,
|
|
7
|
+
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, SEGMENT, MEMBERSHIPS } from '../utils/constants';
|
|
8
8
|
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
|
|
9
|
+
import { authProviderFactory } from './authProvider';
|
|
10
|
+
import { secureSplitHttpClientFactory } from './secureSplitHttpClient';
|
|
9
11
|
|
|
10
12
|
const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
|
|
11
13
|
|
|
12
|
-
function userKeyToQueryParam(userKey: string) {
|
|
13
|
-
return 'users=' + encodeURIComponent(userKey); // no need to check availability of `encodeURIComponent`, since it is a global highly supported.
|
|
14
|
-
}
|
|
15
|
-
|
|
16
14
|
/**
|
|
17
15
|
* Factory of ServiceApi objects, which group the collection of HTTP endpoints used by the SDKs
|
|
18
16
|
*
|
|
19
17
|
* @param settings - validated settings object
|
|
20
18
|
* @param platform - object containing environment-specific dependencies
|
|
21
19
|
* @param telemetryTracker - telemetry tracker
|
|
22
|
-
* @param secureSplitHttpClientFactory - factory of SecureSplitHttpClient objects
|
|
23
20
|
*/
|
|
24
21
|
export function serviceApiFactory(
|
|
25
22
|
settings: ISettings,
|
|
26
23
|
platform: Pick<IPlatform, 'getOptions' | 'getFetch'>,
|
|
27
24
|
telemetryTracker: ITelemetryTracker,
|
|
28
|
-
secureSplitHttpClientFactory?: (settings: ISettings, platform: Pick<IPlatform, 'getOptions' | 'getFetch'>, telemetryTracker: ITelemetryTracker) => ISecureSplitHttpClient,
|
|
29
25
|
): IServiceApi {
|
|
30
26
|
|
|
31
27
|
const urls = settings.urls;
|
|
32
28
|
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
33
29
|
const SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
30
|
+
|
|
34
31
|
const splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
35
|
-
|
|
32
|
+
|
|
33
|
+
// Shared authProvider so that the SSE authentication (via `fetchAuth`,
|
|
34
|
+
// used by `pushManager`) and every authenticated HTTP request to the Configs
|
|
35
|
+
// API reuse the same cached credential instead of each fetching their own.
|
|
36
|
+
const authProvider = authProviderFactory(settings, splitHttpClient, telemetryTracker);
|
|
37
|
+
const secureSplitHttpClient = secureSplitHttpClientFactory(splitHttpClient, authProvider);
|
|
38
|
+
|
|
39
|
+
let initialAuth = true;
|
|
36
40
|
|
|
37
41
|
return {
|
|
42
|
+
fetchAuth() {
|
|
43
|
+
// Guard condition to avoid invalidating the cached credential on the first pushManager authentication
|
|
44
|
+
if (initialAuth) initialAuth = false;
|
|
45
|
+
else authProvider.invalidate();
|
|
46
|
+
|
|
47
|
+
return authProvider.credential();
|
|
48
|
+
},
|
|
49
|
+
|
|
38
50
|
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
39
51
|
getSdkAPIHealthCheck() {
|
|
40
52
|
const url = `${urls.sdk}/api/version`;
|
|
@@ -46,15 +58,6 @@ export function serviceApiFactory(
|
|
|
46
58
|
return splitHttpClient(url).then(() => true).catch(() => false);
|
|
47
59
|
},
|
|
48
60
|
|
|
49
|
-
fetchAuth(userMatchingKeys?: string[]) {
|
|
50
|
-
let url = `${urls.auth}/api/v2/auth?s=${settings.sync.flagSpecVersion}`;
|
|
51
|
-
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
52
|
-
const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
53
|
-
if (queryParams) url += '&' + queryParams;
|
|
54
|
-
}
|
|
55
|
-
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
56
|
-
},
|
|
57
|
-
|
|
58
61
|
fetchSplitChanges(since: number, noCache?: boolean, till?: number, rbSince?: number) {
|
|
59
62
|
const url = `${urls.sdk}/api/splitChanges?s=${settings.sync.flagSpecVersion}&since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
|
|
60
63
|
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
|
|
@@ -99,7 +102,7 @@ export function serviceApiFactory(
|
|
|
99
102
|
*/
|
|
100
103
|
postEventsBulk(body: string, headers?: Record<string, string>) {
|
|
101
104
|
const url = `${urls.events}/api/events/bulk`;
|
|
102
|
-
return
|
|
105
|
+
return secureSplitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(EVENTS), false, false, false);
|
|
103
106
|
},
|
|
104
107
|
|
|
105
108
|
/**
|
|
@@ -110,10 +113,10 @@ export function serviceApiFactory(
|
|
|
110
113
|
*/
|
|
111
114
|
postTestImpressionsBulk(body: string, headers?: Record<string, string>) {
|
|
112
115
|
const url = `${urls.events}/api/testImpressions/bulk`;
|
|
113
|
-
return
|
|
116
|
+
return secureSplitHttpClient(url, {
|
|
114
117
|
// Adding extra headers to send impressions in OPTIMIZED or DEBUG modes.
|
|
115
118
|
method: 'POST', body, headers: objectAssign({ SplitSDKImpressionsMode }, headers)
|
|
116
|
-
}, telemetryTracker.trackHttp(IMPRESSIONS));
|
|
119
|
+
}, telemetryTracker.trackHttp(IMPRESSIONS), false, false, false);
|
|
117
120
|
},
|
|
118
121
|
|
|
119
122
|
/**
|
|
@@ -124,7 +127,7 @@ export function serviceApiFactory(
|
|
|
124
127
|
*/
|
|
125
128
|
postTestImpressionsCount(body: string, headers?: Record<string, string>) {
|
|
126
129
|
const url = `${urls.events}/api/testImpressions/count`;
|
|
127
|
-
return
|
|
130
|
+
return secureSplitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT), false, false, false);
|
|
128
131
|
},
|
|
129
132
|
|
|
130
133
|
/**
|
|
@@ -135,7 +138,7 @@ export function serviceApiFactory(
|
|
|
135
138
|
*/
|
|
136
139
|
postUniqueKeysBulkCs(body: string, headers?: Record<string, string>) {
|
|
137
140
|
const url = `${urls.telemetry}/api/v1/keys/cs`;
|
|
138
|
-
return
|
|
141
|
+
return secureSplitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), false, false, false);
|
|
139
142
|
},
|
|
140
143
|
|
|
141
144
|
/**
|
|
@@ -146,7 +149,7 @@ export function serviceApiFactory(
|
|
|
146
149
|
*/
|
|
147
150
|
postUniqueKeysBulkSs(body: string, headers?: Record<string, string>) {
|
|
148
151
|
const url = `${urls.telemetry}/api/v1/keys/ss`;
|
|
149
|
-
return
|
|
152
|
+
return secureSplitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), false, false, false);
|
|
150
153
|
},
|
|
151
154
|
|
|
152
155
|
postMetricsConfig(body: string, headers?: Record<string, string>) {
|
package/src/services/splitApi.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { objectAssign } from '../utils/lang/objectAssign';
|
|
|
6
6
|
import { ITelemetryTracker } from '../trackers/types';
|
|
7
7
|
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MEMBERSHIPS } from '../utils/constants';
|
|
8
8
|
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
|
|
9
|
+
import { fetchAuthFactory } from '../sync/streaming/AuthClient';
|
|
9
10
|
|
|
10
11
|
const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
|
|
11
12
|
|
|
@@ -33,6 +34,15 @@ export function splitApiFactory(
|
|
|
33
34
|
const splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
34
35
|
|
|
35
36
|
return {
|
|
37
|
+
fetchAuth: fetchAuthFactory((userMatchingKeys?: string[]) => {
|
|
38
|
+
let url = `${urls.auth}/v2/auth?s=${settings.sync.flagSpecVersion}`;
|
|
39
|
+
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
40
|
+
const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
41
|
+
if (queryParams) url += '&' + queryParams;
|
|
42
|
+
}
|
|
43
|
+
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
44
|
+
}),
|
|
45
|
+
|
|
36
46
|
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
37
47
|
getSdkAPIHealthCheck() {
|
|
38
48
|
const url = `${urls.sdk}/version`;
|
|
@@ -44,15 +54,6 @@ export function splitApiFactory(
|
|
|
44
54
|
return splitHttpClient(url).then(() => true).catch(() => false);
|
|
45
55
|
},
|
|
46
56
|
|
|
47
|
-
fetchAuth(userMatchingKeys?: string[]) {
|
|
48
|
-
let url = `${urls.auth}/v2/auth?s=${settings.sync.flagSpecVersion}`;
|
|
49
|
-
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
50
|
-
const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
51
|
-
if (queryParams) url += '&' + queryParams;
|
|
52
|
-
}
|
|
53
|
-
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
54
|
-
},
|
|
55
|
-
|
|
56
57
|
fetchSplitChanges(since: number, noCache?: boolean, till?: number, rbSince?: number) {
|
|
57
58
|
const url = `${urls.sdk}/splitChanges?s=${settings.sync.flagSpecVersion}&since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
|
|
58
59
|
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
|
package/src/services/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { IFetchAuth } from '../sync/streaming/AuthClient/types';
|
|
2
|
+
|
|
1
3
|
export type IRequestOptions = {
|
|
2
4
|
method?: string,
|
|
3
5
|
headers?: Record<string, string>,
|
|
@@ -33,9 +35,8 @@ export type IHealthCheckAPI = () => Promise<boolean>
|
|
|
33
35
|
|
|
34
36
|
export type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader?: boolean) => Promise<IResponse>
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
export type IFetchAuth = (userKeys?: string[]) => Promise<IResponse>
|
|
38
|
+
// @TODO: remove `newVersionHeader` and `useJwt` arguments when all service endpoints are migrated to the new auth mechanism
|
|
39
|
+
export type ISecureSplitHttpClient = ((url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader?: boolean, useJwt?: boolean) => Promise<IResponse>) & { stop(): void }
|
|
39
40
|
|
|
40
41
|
export type IFetchDefinitionChanges = (since: number, noCache?: boolean, till?: number, rbSince?: number) => Promise<IResponse>
|
|
41
42
|
|
|
@@ -73,7 +74,7 @@ export interface IServiceApi {
|
|
|
73
74
|
postTestImpressionsCount: IPostTestImpressionsCount
|
|
74
75
|
postMetricsConfig: IPostMetricsConfig
|
|
75
76
|
postMetricsUsage: IPostMetricsUsage
|
|
76
|
-
// lifecycle
|
|
77
|
+
// lifecycle: stops authProvider backoff retries
|
|
77
78
|
stop(): void
|
|
78
79
|
}
|
|
79
80
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Dynamically require ioredis to prevent strict TypeScript binding
|
|
2
|
-
// and handle module export differences between v4 and
|
|
2
|
+
// and handle module export differences between v4 and later major versions.
|
|
3
3
|
let RedisConstructor: any;
|
|
4
4
|
try {
|
|
5
5
|
const ioredisLib = require('ioredis');
|
|
6
6
|
RedisConstructor = ioredisLib.default || ioredisLib;
|
|
7
7
|
} catch (e) {
|
|
8
8
|
// If we reach here, the peer dependency is missing
|
|
9
|
-
throw new Error('ioredis is missing. Please install ioredis v4 or
|
|
9
|
+
throw new Error('ioredis is missing. Please install ioredis v4, v5 or v6.');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
import { ILogger } from '../../logger/types';
|
|
@@ -31,9 +31,12 @@ const DEFAULT_LIBRARY_OPTIONS = {
|
|
|
31
31
|
enableOfflineQueue: false,
|
|
32
32
|
connectTimeout: DEFAULT_OPTIONS.connectionTimeout,
|
|
33
33
|
lazyConnect: false,
|
|
34
|
-
// CRITICAL: v5
|
|
35
|
-
// v4 defaulted to 5000. We explicitly set it here to ensure v5
|
|
34
|
+
// CRITICAL: v5 and v6 default this to 0 (disabled), which breaks dynamic clusters.
|
|
35
|
+
// v4 defaulted to 5000. We explicitly set it here to ensure v5 and v6 work like v4.
|
|
36
36
|
slotsRefreshInterval: 5000,
|
|
37
|
+
// v6 defaults to RESP3 (`protocol: 3`). The SDK doesn't use any RESP3 feature,
|
|
38
|
+
// so we stick to RESP2 to keep the v4 and v5 behavior. Ignored by v4 and v5.
|
|
39
|
+
protocol: 2,
|
|
37
40
|
};
|
|
38
41
|
|
|
39
42
|
interface IRedisCommand {
|
|
@@ -45,7 +48,7 @@ interface IRedisCommand {
|
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
50
|
* Redis adapter on top of the library of choice (written with ioredis) for some extra control.
|
|
48
|
-
* Refactored to use Composition instead of Inheritance to support
|
|
51
|
+
* Refactored to use Composition instead of Inheritance to support v4, v5 and v6.
|
|
49
52
|
*/
|
|
50
53
|
export class RedisAdapter {
|
|
51
54
|
// eslint-disable-next-line no-undef -- Index signature to allow proxying dynamic ioredis methods without TS errors
|