@splitsoftware/splitio-commons 3.1.1-rc.1 → 3.1.1-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +12 -1
- package/cjs/evaluator/index.js +11 -6
- package/cjs/presets/serverSide.js +3 -3
- package/cjs/sdkClient/client.js +3 -5
- package/cjs/sdkClient/sdkLifecycle.js +2 -0
- package/cjs/services/authProvider.js +23 -10
- package/cjs/services/secureSplitHttpClient.js +10 -13
- package/cjs/services/serviceApi.js +23 -21
- package/cjs/services/splitApi.js +10 -9
- package/cjs/storages/inRedis/RedisAdapter.js +8 -5
- package/cjs/sync/polling/pollingManagerCS.js +84 -79
- package/cjs/sync/polling/pollingManagerSS.js +43 -35
- package/cjs/sync/polling/updaters/definitionChangesUpdater.js +2 -2
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/cjs/sync/streaming/AuthClient/index.js +13 -14
- package/cjs/sync/streaming/SSEHandler/index.js +1 -0
- package/cjs/sync/streaming/constants.js +2 -1
- package/cjs/sync/streaming/pushManager.js +5 -10
- package/cjs/sync/submitters/impressionsSubmitter.js +1 -1
- package/cjs/sync/syncManagerOnline.js +2 -7
- package/cjs/utils/Backoff.js +16 -4
- package/esm/evaluator/index.js +11 -6
- package/esm/presets/serverSide.js +3 -3
- package/esm/sdkClient/client.js +3 -5
- package/esm/sdkClient/sdkLifecycle.js +2 -0
- package/esm/services/authProvider.js +24 -11
- package/esm/services/secureSplitHttpClient.js +10 -13
- package/esm/services/serviceApi.js +24 -22
- package/esm/services/splitApi.js +10 -9
- package/esm/storages/inRedis/RedisAdapter.js +8 -5
- package/esm/sync/polling/pollingManagerCS.js +84 -79
- package/esm/sync/polling/pollingManagerSS.js +43 -35
- package/esm/sync/polling/updaters/definitionChangesUpdater.js +3 -3
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
- package/esm/sync/streaming/AuthClient/index.js +11 -11
- package/esm/sync/streaming/SSEHandler/index.js +2 -1
- package/esm/sync/streaming/constants.js +1 -0
- package/esm/sync/streaming/pushManager.js +6 -11
- package/esm/sync/submitters/impressionsSubmitter.js +1 -1
- package/esm/sync/syncManagerOnline.js +2 -7
- package/esm/utils/Backoff.js +16 -4
- 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
|
@@ -4,46 +4,54 @@ exports.pollingManagerSSFactory = void 0;
|
|
|
4
4
|
var definitionsSyncTask_1 = require("./syncTasks/definitionsSyncTask");
|
|
5
5
|
var segmentsSyncTask_1 = require("./syncTasks/segmentsSyncTask");
|
|
6
6
|
var constants_1 = require("../../logger/constants");
|
|
7
|
+
var segmentChangesFetcher_1 = require("./fetchers/segmentChangesFetcher");
|
|
8
|
+
var splitChangesFetcher_1 = require("./fetchers/splitChangesFetcher");
|
|
7
9
|
/**
|
|
8
10
|
* Expose start / stop mechanism for pulling data from services.
|
|
9
11
|
*/
|
|
10
|
-
function pollingManagerSSFactory(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
12
|
+
function pollingManagerSSFactory(definitionFetcherFactory, segmentFetcherFactory) {
|
|
13
|
+
if (definitionFetcherFactory === void 0) { definitionFetcherFactory = splitChangesFetcher_1.splitChangesFetcherFactory; }
|
|
14
|
+
if (segmentFetcherFactory === void 0) { segmentFetcherFactory = function (params) { return (0, segmentChangesFetcher_1.segmentChangesFetcherFactory)(params.serviceApi.fetchSegmentChanges); }; }
|
|
15
|
+
return function (params) {
|
|
16
|
+
var storage = params.storage, readiness = params.readiness, settings = params.settings;
|
|
17
|
+
var log = settings.log;
|
|
18
|
+
var definitionChangesFetcher = definitionFetcherFactory(params);
|
|
19
|
+
var segmentChangesFetcher = segmentFetcherFactory(params);
|
|
20
|
+
var segmentsSyncTask = (0, segmentsSyncTask_1.segmentsSyncTaskFactory)(segmentChangesFetcher, storage, readiness, settings);
|
|
21
|
+
var definitionsSyncTask = (0, definitionsSyncTask_1.definitionsSyncTaskFactory)(definitionChangesFetcher, storage, readiness, settings, segmentsSyncTask);
|
|
22
|
+
return {
|
|
23
|
+
definitionsSyncTask: definitionsSyncTask,
|
|
24
|
+
segmentsSyncTask: segmentsSyncTask,
|
|
25
|
+
// Start periodic fetching (polling)
|
|
26
|
+
start: function () {
|
|
27
|
+
log.info(constants_1.POLLING_START);
|
|
28
|
+
log.debug(constants_1.LOG_PREFIX_SYNC_POLLING + "".concat(definitionChangesFetcher.type, " will be refreshed each ").concat(settings.scheduler.featuresRefreshRate, " millis"));
|
|
29
|
+
log.debug(constants_1.LOG_PREFIX_SYNC_POLLING + "segments will be refreshed each ".concat(settings.scheduler.segmentsRefreshRate, " millis"));
|
|
30
|
+
var startingUp = definitionsSyncTask.start();
|
|
31
|
+
if (startingUp) {
|
|
32
|
+
startingUp.then(function () {
|
|
33
|
+
if (definitionsSyncTask.isRunning())
|
|
34
|
+
segmentsSyncTask.start();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
// Stop periodic fetching (polling)
|
|
39
|
+
stop: function () {
|
|
40
|
+
log.info(constants_1.POLLING_STOP);
|
|
41
|
+
if (definitionsSyncTask.isRunning())
|
|
42
|
+
definitionsSyncTask.stop();
|
|
43
|
+
if (segmentsSyncTask.isRunning())
|
|
44
|
+
segmentsSyncTask.stop();
|
|
45
|
+
},
|
|
46
|
+
// Used by SyncManager to know if running in polling mode.
|
|
47
|
+
isRunning: definitionsSyncTask.isRunning,
|
|
48
|
+
syncAll: function () {
|
|
49
|
+
// fetch definitions and segments. There is no need to catch this promise (`DefinitionChangesUpdater` is always resolved with a boolean value)
|
|
50
|
+
return definitionsSyncTask.execute().then(function () {
|
|
51
|
+
return segmentsSyncTask.execute();
|
|
28
52
|
});
|
|
29
53
|
}
|
|
30
|
-
}
|
|
31
|
-
// Stop periodic fetching (polling)
|
|
32
|
-
stop: function () {
|
|
33
|
-
log.info(constants_1.POLLING_STOP);
|
|
34
|
-
if (definitionsSyncTask.isRunning())
|
|
35
|
-
definitionsSyncTask.stop();
|
|
36
|
-
if (segmentsSyncTask.isRunning())
|
|
37
|
-
segmentsSyncTask.stop();
|
|
38
|
-
},
|
|
39
|
-
// Used by SyncManager to know if running in polling mode.
|
|
40
|
-
isRunning: definitionsSyncTask.isRunning,
|
|
41
|
-
syncAll: function () {
|
|
42
|
-
// fetch definitions and segments. There is no need to catch this promise (`DefinitionChangesUpdater` is always resolved with a boolean value)
|
|
43
|
-
return definitionsSyncTask.execute().then(function () {
|
|
44
|
-
return segmentsSyncTask.execute();
|
|
45
|
-
});
|
|
46
|
-
}
|
|
54
|
+
};
|
|
47
55
|
};
|
|
48
56
|
}
|
|
49
57
|
exports.pollingManagerSSFactory = pollingManagerSSFactory;
|
|
@@ -125,14 +125,14 @@ function definitionChangesUpdaterFactory(log, definitionChangesFetcher, storage,
|
|
|
125
125
|
var since = sinces[0], rbSince = sinces[1];
|
|
126
126
|
log.debug(constants_2.SYNC_FETCH, [definitionChangesFetcher.type, since, rbSince]);
|
|
127
127
|
return Promise.resolve(instantUpdate ?
|
|
128
|
-
instantUpdate.type === constants_4.
|
|
128
|
+
instantUpdate.type === constants_4.RB_SEGMENT_UPDATE ?
|
|
129
|
+
{ rbs: convertInstantUpdateToDefinitionChanges(instantUpdate) } :
|
|
129
130
|
// IFFU edge case: a change to definition that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
|
|
130
131
|
Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, constants_3.IN_RULE_BASED_SEGMENT))).then(function (contains) {
|
|
131
132
|
return contains ?
|
|
132
133
|
{ d: convertInstantUpdateToDefinitionChanges(instantUpdate) } :
|
|
133
134
|
definitionChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
|
|
134
135
|
}) :
|
|
135
|
-
{ rbs: convertInstantUpdateToDefinitionChanges(instantUpdate) } :
|
|
136
136
|
definitionChangesFetcher(since, noCache, till, rbSince, _promiseDecorator))
|
|
137
137
|
.then(function (definitionChanges) {
|
|
138
138
|
var usedSegments = new Set();
|
|
@@ -31,7 +31,7 @@ function segmentChangesUpdaterFactory(log, segmentChangesFetcher, segments, read
|
|
|
31
31
|
false :
|
|
32
32
|
segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then(function (changes) {
|
|
33
33
|
return Promise.all(changes.map(function (x) {
|
|
34
|
-
log.debug("".concat(constants_2.LOG_PREFIX_SYNC, "Processing ").concat(segmentName, " with till = ").concat(x.till, ". Added: ").concat(x.added.length, ". Removed: ").concat(x.removed.length));
|
|
34
|
+
log.debug("".concat(constants_2.LOG_PREFIX_SYNC, "Processing ").concat(segmentName, " with since = ").concat(x.since, " and till = ").concat(x.till, ". Added: ").concat(x.added.length, ". Removed: ").concat(x.removed.length));
|
|
35
35
|
return segments.update(segmentName, x.added, x.removed, x.till);
|
|
36
36
|
})).then(function (updates) {
|
|
37
37
|
return updates.some(function (update) { return update; });
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.fetchAuthFactory = void 0;
|
|
4
4
|
var objectAssign_1 = require("../../../utils/lang/objectAssign");
|
|
5
|
-
var base64_1 = require("../../../utils/base64");
|
|
6
5
|
var jwt_1 = require("../../../utils/jwt");
|
|
7
|
-
var murmur3_1 = require("../../../utils/murmur3/murmur3");
|
|
8
6
|
/**
|
|
9
7
|
* Factory of authentication function.
|
|
10
8
|
*
|
|
11
|
-
* @param fetchAuth -
|
|
9
|
+
* @param fetchAuth - /auth endpoint
|
|
12
10
|
*/
|
|
13
|
-
function
|
|
11
|
+
function fetchAuthFactory(fetchAuth) {
|
|
14
12
|
/**
|
|
15
13
|
* Run authentication requests to Auth Server, and returns a promise that resolves with the decoded JTW token.
|
|
16
14
|
* @param userKeys - set of user Keys to track membership updates. It is undefined for server-side API.
|
|
@@ -24,20 +22,21 @@ function authenticateFactory(fetchAuth) {
|
|
|
24
22
|
if (typeof decodedToken.iat !== 'number' || typeof decodedToken.exp !== 'number')
|
|
25
23
|
throw new Error('token properties "issuedAt" (iat) or "expiration" (exp) are missing or invalid');
|
|
26
24
|
var channels = JSON.parse(decodedToken['x-ably-capability']);
|
|
27
|
-
|
|
25
|
+
var credential = (0, objectAssign_1.objectAssign)({
|
|
28
26
|
decodedToken: decodedToken,
|
|
29
27
|
channels: channels
|
|
30
28
|
}, json);
|
|
29
|
+
// The `/api/v3/auth` endpoint nests the streaming settings under `config.streaming`. Normalize
|
|
30
|
+
// them into the flat `pushEnabled`/`connDelay` properties used by the PushManager.
|
|
31
|
+
var streaming = credential.config && credential.config.streaming;
|
|
32
|
+
if (streaming) {
|
|
33
|
+
credential.pushEnabled = streaming.enabled;
|
|
34
|
+
credential.connDelay = streaming.delay;
|
|
35
|
+
}
|
|
36
|
+
return credential;
|
|
31
37
|
}
|
|
32
38
|
return json;
|
|
33
39
|
});
|
|
34
40
|
};
|
|
35
41
|
}
|
|
36
|
-
exports.
|
|
37
|
-
/**
|
|
38
|
-
* Returns the hash of a given user key
|
|
39
|
-
*/
|
|
40
|
-
function hashUserKey(userKey) {
|
|
41
|
-
return (0, base64_1.encodeToBase64)((0, murmur3_1.hash)(userKey, 0).toString());
|
|
42
|
-
}
|
|
43
|
-
exports.hashUserKey = hashUserKey;
|
|
42
|
+
exports.fetchAuthFactory = fetchAuthFactory;
|
|
@@ -74,6 +74,7 @@ function SSEHandlerFactory(log, pushEmitter, telemetryTracker) {
|
|
|
74
74
|
switch (parsedData.type) {
|
|
75
75
|
/* update events */
|
|
76
76
|
case constants_1.SPLIT_UPDATE:
|
|
77
|
+
case constants_1.CONFIG_UPDATE:
|
|
77
78
|
case constants_1.SEGMENT_UPDATE:
|
|
78
79
|
case constants_1.MEMBERSHIPS_MS_UPDATE:
|
|
79
80
|
case constants_1.MEMBERSHIPS_LS_UPDATE:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ControlType = exports.OCCUPANCY = exports.CONTROL = exports.RB_SEGMENT_UPDATE = exports.SPLIT_UPDATE = exports.SPLIT_KILL = exports.SEGMENT_UPDATE = exports.MEMBERSHIPS_LS_UPDATE = exports.MEMBERSHIPS_MS_UPDATE = exports.PUSH_SUBSYSTEM_DOWN = exports.PUSH_SUBSYSTEM_UP = exports.PUSH_RETRYABLE_ERROR = exports.PUSH_NON_RETRYABLE_ERROR = exports.SECONDS_BEFORE_EXPIRATION = void 0;
|
|
3
|
+
exports.ControlType = exports.OCCUPANCY = exports.CONTROL = exports.RB_SEGMENT_UPDATE = exports.CONFIG_UPDATE = exports.SPLIT_UPDATE = exports.SPLIT_KILL = exports.SEGMENT_UPDATE = exports.MEMBERSHIPS_LS_UPDATE = exports.MEMBERSHIPS_MS_UPDATE = exports.PUSH_SUBSYSTEM_DOWN = exports.PUSH_SUBSYSTEM_UP = exports.PUSH_RETRYABLE_ERROR = exports.PUSH_NON_RETRYABLE_ERROR = exports.SECONDS_BEFORE_EXPIRATION = void 0;
|
|
4
4
|
// time for refresh token
|
|
5
5
|
exports.SECONDS_BEFORE_EXPIRATION = 600;
|
|
6
6
|
// Internal SDK events, subscribed by SyncManager and PushManager
|
|
@@ -30,6 +30,7 @@ exports.MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
|
|
|
30
30
|
exports.SEGMENT_UPDATE = 'SEGMENT_UPDATE';
|
|
31
31
|
exports.SPLIT_KILL = 'SPLIT_KILL';
|
|
32
32
|
exports.SPLIT_UPDATE = 'SPLIT_UPDATE';
|
|
33
|
+
exports.CONFIG_UPDATE = 'CONFIG_UPDATE';
|
|
33
34
|
exports.RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
|
|
34
35
|
// Control-type push notifications, handled by NotificationKeeper
|
|
35
36
|
exports.CONTROL = 'CONTROL';
|
|
@@ -7,7 +7,6 @@ var SSEHandler_1 = require("./SSEHandler");
|
|
|
7
7
|
var MySegmentsUpdateWorker_1 = require("./UpdateWorkers/MySegmentsUpdateWorker");
|
|
8
8
|
var SegmentsUpdateWorker_1 = require("./UpdateWorkers/SegmentsUpdateWorker");
|
|
9
9
|
var DefinitionsUpdateWorker_1 = require("./UpdateWorkers/DefinitionsUpdateWorker");
|
|
10
|
-
var AuthClient_1 = require("./AuthClient");
|
|
11
10
|
var lang_1 = require("../../utils/lang");
|
|
12
11
|
var SSEClient_1 = require("./SSEClient");
|
|
13
12
|
var key_1 = require("../../utils/key");
|
|
@@ -37,7 +36,6 @@ function pushManagerFactory(params, pollingManager) {
|
|
|
37
36
|
log.warn(constants_2.STREAMING_FALLBACK, [e]);
|
|
38
37
|
return;
|
|
39
38
|
}
|
|
40
|
-
var authenticate = (0, AuthClient_1.authenticateFactory)(serviceApi.fetchAuth);
|
|
41
39
|
// init feedback loop
|
|
42
40
|
var pushEmitter = new platform.EventEmitter();
|
|
43
41
|
var sseHandler = (0, SSEHandler_1.SSEHandlerFactory)(log, pushEmitter, telemetryTracker);
|
|
@@ -47,8 +45,6 @@ function pushManagerFactory(params, pollingManager) {
|
|
|
47
45
|
var segmentsUpdateWorker = userKey ? undefined : (0, SegmentsUpdateWorker_1.SegmentsUpdateWorker)(log, pollingManager.segmentsSyncTask, storage.segments);
|
|
48
46
|
// For server-side we pass the segmentsSyncTask, used by DefinitionsUpdateWorker to fetch new segments
|
|
49
47
|
var definitionsUpdateWorker = (0, DefinitionsUpdateWorker_1.DefinitionsUpdateWorker)(log, storage, pollingManager.definitionsSyncTask, readiness.definitions, telemetryTracker);
|
|
50
|
-
// [Only for client-side] map of hashes to user keys, to dispatch membership update events to the corresponding MySegmentsUpdateWorker
|
|
51
|
-
var userKeyHashes = {};
|
|
52
48
|
// [Only for client-side] map of user keys to their corresponding hash64 and MySegmentsUpdateWorkers.
|
|
53
49
|
// Hash64 is used to process membership update events and dispatch actions to the corresponding MySegmentsUpdateWorker.
|
|
54
50
|
var clients = {};
|
|
@@ -93,7 +89,7 @@ function pushManagerFactory(params, pollingManager) {
|
|
|
93
89
|
log.info(constants_2.STREAMING_CONNECTING);
|
|
94
90
|
disconnected = false;
|
|
95
91
|
var userKeys = userKey ? Object.keys(clients) : undefined;
|
|
96
|
-
|
|
92
|
+
serviceApi.fetchAuth(userKeys).then(function (authData) {
|
|
97
93
|
if (disconnected)
|
|
98
94
|
return;
|
|
99
95
|
// 'pushEnabled: false' is handled as a PUSH_NON_RETRYABLE_ERROR instead of PUSH_SUBSYSTEM_DOWN, in order to
|
|
@@ -181,8 +177,11 @@ function pushManagerFactory(params, pollingManager) {
|
|
|
181
177
|
connectPush();
|
|
182
178
|
});
|
|
183
179
|
/** Functions related to synchronization (Queues and Workers in the spec) */
|
|
180
|
+
// Some notification types are specific to the Feature flags or Configs SDKs. Their handlers are
|
|
181
|
+
// registered unconditionally, since the SDK only subscribes to the channels of its own entities.
|
|
184
182
|
pushEmitter.on(constants_1.SPLIT_KILL, definitionsUpdateWorker.killDefinition);
|
|
185
183
|
pushEmitter.on(constants_1.SPLIT_UPDATE, definitionsUpdateWorker.put);
|
|
184
|
+
pushEmitter.on(constants_1.CONFIG_UPDATE, definitionsUpdateWorker.put);
|
|
186
185
|
pushEmitter.on(constants_1.RB_SEGMENT_UPDATE, definitionsUpdateWorker.put);
|
|
187
186
|
function handleMySegmentsUpdate(parsedData) {
|
|
188
187
|
switch (parsedData.u) {
|
|
@@ -285,9 +284,7 @@ function pushManagerFactory(params, pollingManager) {
|
|
|
285
284
|
},
|
|
286
285
|
// [Only for client-side]
|
|
287
286
|
add: function (userKey, mySegmentsSyncTask) {
|
|
288
|
-
|
|
289
|
-
if (!userKeyHashes[hash]) {
|
|
290
|
-
userKeyHashes[hash] = userKey;
|
|
287
|
+
if (!clients[userKey]) {
|
|
291
288
|
clients[userKey] = {
|
|
292
289
|
hash64: (0, murmur3_64_1.hash64)(userKey),
|
|
293
290
|
worker: (0, MySegmentsUpdateWorker_1.MySegmentsUpdateWorker)(log, storage, mySegmentsSyncTask, telemetryTracker)
|
|
@@ -308,8 +305,6 @@ function pushManagerFactory(params, pollingManager) {
|
|
|
308
305
|
},
|
|
309
306
|
// [Only for client-side]
|
|
310
307
|
remove: function (userKey) {
|
|
311
|
-
var hash = (0, AuthClient_1.hashUserKey)(userKey);
|
|
312
|
-
delete userKeyHashes[hash];
|
|
313
308
|
delete clients[userKey];
|
|
314
309
|
}
|
|
315
310
|
});
|
|
@@ -23,7 +23,7 @@ function fromImpressionsCollector(sendLabels, data) {
|
|
|
23
23
|
b: entry.bucketingKey,
|
|
24
24
|
pt: entry.pt,
|
|
25
25
|
properties: entry.properties,
|
|
26
|
-
// @
|
|
26
|
+
// @TODO: add entityType to public SplitIO.ImpressionDTO type
|
|
27
27
|
et: entry.entityType, // Definition type
|
|
28
28
|
};
|
|
29
29
|
})
|
|
@@ -8,8 +8,6 @@ var consent_1 = require("../consent");
|
|
|
8
8
|
var constants_3 = require("../utils/constants");
|
|
9
9
|
var constants_4 = require("../readiness/constants");
|
|
10
10
|
var AbstractDefinitionsCacheSync_1 = require("../storages/AbstractDefinitionsCacheSync");
|
|
11
|
-
var splitChangesFetcher_1 = require("./polling/fetchers/splitChangesFetcher");
|
|
12
|
-
var segmentChangesFetcher_1 = require("./polling/fetchers/segmentChangesFetcher");
|
|
13
11
|
/**
|
|
14
12
|
* Online SyncManager factory.
|
|
15
13
|
* Can be used for server-side API, and client-side API with or without multiple clients.
|
|
@@ -17,18 +15,15 @@ var segmentChangesFetcher_1 = require("./polling/fetchers/segmentChangesFetcher"
|
|
|
17
15
|
* @param pollingManagerFactory - allows to specialize the SyncManager for server-side or client-side API by passing
|
|
18
16
|
* `pollingManagerSSFactory` or `pollingManagerCSFactory` respectively.
|
|
19
17
|
* @param pushManagerFactory - optional to build a SyncManager with or without streaming support
|
|
20
|
-
* @param definitionChangesFetcherFactory - optional to replace the default split changes fetcher
|
|
21
18
|
*/
|
|
22
|
-
function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory
|
|
23
|
-
if (definitionFetcherFactory === void 0) { definitionFetcherFactory = splitChangesFetcher_1.splitChangesFetcherFactory; }
|
|
24
|
-
if (segmentFetcherFactory === void 0) { segmentFetcherFactory = function (params) { return (0, segmentChangesFetcher_1.segmentChangesFetcherFactory)(params.serviceApi.fetchSegmentChanges); }; }
|
|
19
|
+
function syncManagerOnlineFactory(pollingManagerFactory, pushManagerFactory) {
|
|
25
20
|
/**
|
|
26
21
|
* SyncManager factory for modular SDK
|
|
27
22
|
*/
|
|
28
23
|
return function (params) {
|
|
29
24
|
var settings = params.settings, _a = params.settings, log = _a.log, streamingEnabled = _a.streamingEnabled, syncEnabled = _a.sync.enabled, telemetryTracker = params.telemetryTracker, storage = params.storage, readiness = params.readiness;
|
|
30
25
|
/** Polling Manager */
|
|
31
|
-
var pollingManager = pollingManagerFactory && pollingManagerFactory(params
|
|
26
|
+
var pollingManager = pollingManagerFactory && pollingManagerFactory(params);
|
|
32
27
|
/** Push Manager */
|
|
33
28
|
var pushManager = syncEnabled && streamingEnabled && pollingManager && pushManagerFactory ?
|
|
34
29
|
pushManagerFactory(params, pollingManager) :
|
package/cjs/utils/Backoff.js
CHANGED
|
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Backoff = void 0;
|
|
4
4
|
var Backoff = /** @class */ (function () {
|
|
5
5
|
/**
|
|
6
|
-
* Schedule function calls with exponential backoff
|
|
6
|
+
* Schedule function calls with exponential backoff.
|
|
7
|
+
* @param cb - The function to call. Must return a promise if used with `scheduleCallAsync`.
|
|
7
8
|
*/
|
|
8
9
|
function Backoff(cb, baseMillis, maxMillis) {
|
|
9
10
|
this.baseMillis = Backoff.__TEST__BASE_MILLIS || baseMillis || Backoff.DEFAULT_BASE_MILLIS;
|
|
@@ -20,11 +21,11 @@ var Backoff = /** @class */ (function () {
|
|
|
20
21
|
var delayInMillis = Math.min(this.baseMillis * Math.pow(2, this.attempts), this.maxMillis);
|
|
21
22
|
if (this.timeoutID)
|
|
22
23
|
clearTimeout(this.timeoutID);
|
|
24
|
+
this.attempts++;
|
|
23
25
|
this.timeoutID = setTimeout(function () {
|
|
24
26
|
_this.timeoutID = undefined;
|
|
25
27
|
_this.cb();
|
|
26
28
|
}, delayInMillis);
|
|
27
|
-
this.attempts++;
|
|
28
29
|
return delayInMillis;
|
|
29
30
|
};
|
|
30
31
|
/**
|
|
@@ -38,21 +39,32 @@ var Backoff = /** @class */ (function () {
|
|
|
38
39
|
clearTimeout(this.timeoutID);
|
|
39
40
|
this.attempts++;
|
|
40
41
|
return new Promise(function (resolve, reject) {
|
|
42
|
+
_this.pending = { resolve: resolve, reject: reject };
|
|
41
43
|
_this.timeoutID = setTimeout(function () {
|
|
42
44
|
_this.timeoutID = undefined;
|
|
45
|
+
_this.pending = undefined;
|
|
43
46
|
_this.cb().then(resolve, reject);
|
|
44
47
|
}, delayInMillis);
|
|
45
48
|
});
|
|
46
49
|
};
|
|
47
50
|
/**
|
|
48
|
-
* Reset the backoff attempts
|
|
51
|
+
* Reset the backoff attempts, canceling any scheduled `cb` call. If a `scheduleCallAsync` call is pending
|
|
52
|
+
* and the caller wants to settle it immediately instead of waiting for `cb` to be invoked, pass `settle`:
|
|
53
|
+
* `{ value }` to resolve, or `{ error }` to reject.
|
|
49
54
|
*/
|
|
50
|
-
Backoff.prototype.reset = function () {
|
|
55
|
+
Backoff.prototype.reset = function (settle) {
|
|
51
56
|
this.attempts = 0;
|
|
52
57
|
if (this.timeoutID) {
|
|
53
58
|
clearTimeout(this.timeoutID);
|
|
54
59
|
this.timeoutID = undefined;
|
|
55
60
|
}
|
|
61
|
+
if (settle && this.pending) {
|
|
62
|
+
if ('value' in settle)
|
|
63
|
+
this.pending.resolve(settle.value);
|
|
64
|
+
else
|
|
65
|
+
this.pending.reject(settle.error);
|
|
66
|
+
this.pending = undefined;
|
|
67
|
+
}
|
|
56
68
|
};
|
|
57
69
|
Backoff.DEFAULT_BASE_MILLIS = 1000; // 1 second
|
|
58
70
|
Backoff.DEFAULT_MAX_MILLIS = 1800000; // 30 minutes
|
package/esm/evaluator/index.js
CHANGED
|
@@ -22,6 +22,12 @@ function treatmentsException(definitionNames) {
|
|
|
22
22
|
});
|
|
23
23
|
return evaluations;
|
|
24
24
|
}
|
|
25
|
+
function setEvaluationDataFromDefinition(evaluation, definition) {
|
|
26
|
+
return objectAssign(evaluation, {
|
|
27
|
+
definition: definition,
|
|
28
|
+
config: definition.configurations && definition.configurations[evaluation.treatment] || null
|
|
29
|
+
});
|
|
30
|
+
}
|
|
25
31
|
export function evaluateFeature(log, key, definitionName, attributes, storage) {
|
|
26
32
|
var definition;
|
|
27
33
|
try {
|
|
@@ -92,8 +98,8 @@ function getEvaluation(log, key, definition, attributes, storage) {
|
|
|
92
98
|
var split = engineParser(log, definition, storage);
|
|
93
99
|
var evaluation = split.getTreatment(key, attributes, evaluateFeature);
|
|
94
100
|
return thenable(evaluation) ?
|
|
95
|
-
evaluation.then(function (result) { return
|
|
96
|
-
|
|
101
|
+
evaluation.then(function (result) { return setEvaluationDataFromDefinition(result, definition); }) :
|
|
102
|
+
setEvaluationDataFromDefinition(evaluation, definition);
|
|
97
103
|
}
|
|
98
104
|
return EVALUATION_DEFINITION_NOT_FOUND;
|
|
99
105
|
}
|
|
@@ -124,11 +130,10 @@ export function evaluateDefaultTreatment(definitionName, storage) {
|
|
|
124
130
|
}
|
|
125
131
|
function getDefaultTreatment(definition) {
|
|
126
132
|
if (definition) {
|
|
127
|
-
return {
|
|
133
|
+
return setEvaluationDataFromDefinition({
|
|
128
134
|
treatment: definition.defaultTreatment,
|
|
129
|
-
label: NO_CONDITION_MATCH,
|
|
130
|
-
|
|
131
|
-
};
|
|
135
|
+
label: NO_CONDITION_MATCH, // "default rule"
|
|
136
|
+
}, definition);
|
|
132
137
|
}
|
|
133
138
|
return EVALUATION_DEFINITION_NOT_FOUND;
|
|
134
139
|
}
|
|
@@ -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';
|
|
@@ -6,10 +6,10 @@ import { InMemoryStorageFactory } from '../storages/inMemory/InMemoryStorage';
|
|
|
6
6
|
import { sdkManagerFactory } from '../sdkManager';
|
|
7
7
|
import { sdkClientMethodFactory } from '../sdkClient/sdkClientMethod';
|
|
8
8
|
import { impressionObserverSSFactory } from '../trackers/impressionObserver/impressionObserverSS';
|
|
9
|
-
var syncManagerOnlineSSFactory = syncManagerOnlineFactory(pollingManagerSSFactory, pushManagerFactory);
|
|
9
|
+
var syncManagerOnlineSSFactory = syncManagerOnlineFactory(pollingManagerSSFactory(), pushManagerFactory);
|
|
10
10
|
export var serverSideModules = {
|
|
11
11
|
storageFactory: InMemoryStorageFactory,
|
|
12
|
-
serviceApiFactory:
|
|
12
|
+
serviceApiFactory: splitApiFactory,
|
|
13
13
|
syncManagerFactory: syncManagerOnlineSSFactory,
|
|
14
14
|
sdkManagerFactory: sdkManagerFactory,
|
|
15
15
|
sdkClientMethodFactory: sdkClientMethodFactory,
|
package/esm/sdkClient/client.js
CHANGED
|
@@ -108,13 +108,11 @@ export function clientFactory(params) {
|
|
|
108
108
|
}
|
|
109
109
|
// Internal function
|
|
110
110
|
function processEvaluation(evaluation, featureFlagName, key, withConfig, invokingMethodName, queue, options) {
|
|
111
|
-
var _a;
|
|
112
111
|
if (options === void 0) { options = {}; }
|
|
113
112
|
var matchingKey = getMatching(key);
|
|
114
113
|
var bucketingKey = getBucketing(key);
|
|
115
114
|
var definition = evaluation.definition;
|
|
116
|
-
var treatment = evaluation.treatment, label = evaluation.label;
|
|
117
|
-
var config = ((_a = definition === null || definition === void 0 ? void 0 : definition.configurations) === null || _a === void 0 ? void 0 : _a[treatment]) || null;
|
|
115
|
+
var treatment = evaluation.treatment, label = evaluation.label, _a = evaluation.config, config = _a === void 0 ? null : _a;
|
|
118
116
|
if (treatment === CONTROL) {
|
|
119
117
|
var fallbackTreatment = fallbackCalculator(featureFlagName, label);
|
|
120
118
|
treatment = fallbackTreatment.treatment;
|
|
@@ -131,10 +129,10 @@ export function clientFactory(params) {
|
|
|
131
129
|
time: Date.now(),
|
|
132
130
|
bucketingKey: bucketingKey,
|
|
133
131
|
label: label,
|
|
134
|
-
changeNumber: definition.changeNumber,
|
|
132
|
+
changeNumber: definition === null || definition === void 0 ? void 0 : definition.changeNumber,
|
|
135
133
|
properties: stringify(options.properties)
|
|
136
134
|
},
|
|
137
|
-
disabled: options.impressionsDisabled || definition.impressionsDisabled
|
|
135
|
+
disabled: options.impressionsDisabled || (definition === null || definition === void 0 ? void 0 : definition.impressionsDisabled)
|
|
138
136
|
});
|
|
139
137
|
}
|
|
140
138
|
if (withConfig) {
|
|
@@ -58,6 +58,8 @@ export function sdkLifecycleFactory(params, isSharedClient) {
|
|
|
58
58
|
}
|
|
59
59
|
// Stop background jobs
|
|
60
60
|
syncManager && syncManager.stop();
|
|
61
|
+
// Stop the service API (the auth provider backoff retries specifically) before flushing,
|
|
62
|
+
// so that a pending/never-resolved auth retry doesn't block the flush forever
|
|
61
63
|
serviceApi && serviceApi.stop();
|
|
62
64
|
return __flush().then(function () {
|
|
63
65
|
// Cleanup storage
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fetchAuthFactory } from '../sync/streaming/AuthClient';
|
|
2
2
|
import { Backoff } from '../utils/Backoff';
|
|
3
3
|
import { LOG_PREFIX_SYNC_AUTH } from '../logger/constants';
|
|
4
4
|
import { TOKEN } from '../utils/constants';
|
|
@@ -6,32 +6,35 @@ var SKEW_SECONDS = 30;
|
|
|
6
6
|
function isExpired(credential) {
|
|
7
7
|
return Date.now() / 1000 + SKEW_SECONDS >= credential.decodedToken.exp;
|
|
8
8
|
}
|
|
9
|
+
function noCachedCredentialError() {
|
|
10
|
+
return new Error(LOG_PREFIX_SYNC_AUTH + 'stopped without a cached credential');
|
|
11
|
+
}
|
|
9
12
|
/**
|
|
10
13
|
* Factory of AuthProvider, which provides JWT credentials for authenticated HTTP requests.
|
|
11
14
|
* Credentials are fetched lazily on demand, cached in memory, and retried with backoff on failure.
|
|
12
15
|
*/
|
|
13
16
|
export function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
|
|
14
17
|
var urls = settings.urls, log = settings.log;
|
|
15
|
-
|
|
18
|
+
var fetchAuth = fetchAuthFactory(function () {
|
|
16
19
|
var url = "".concat(urls.auth, "/api/v3/auth?capabilities=config,aiconfig");
|
|
17
20
|
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN), false, true);
|
|
18
|
-
}
|
|
19
|
-
var authenticate = authenticateFactory(fetchAuth);
|
|
21
|
+
});
|
|
20
22
|
var backoff = new Backoff(fetchCredential);
|
|
21
23
|
var cachedCredential;
|
|
22
24
|
var inFlightPromise;
|
|
23
25
|
var stopped = false;
|
|
24
26
|
function fetchCredential() {
|
|
25
|
-
return
|
|
27
|
+
return fetchAuth().then(function (credential) {
|
|
26
28
|
log.info(LOG_PREFIX_SYNC_AUTH + 'credential fetched successfully');
|
|
27
29
|
cachedCredential = credential;
|
|
28
30
|
inFlightPromise = undefined;
|
|
29
31
|
backoff.reset();
|
|
30
32
|
return credential;
|
|
31
33
|
}).catch(function (error) {
|
|
32
|
-
// Avoid
|
|
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).
|
|
33
36
|
if (stopped)
|
|
34
|
-
return cachedCredential;
|
|
37
|
+
return cachedCredential || Promise.reject(noCachedCredentialError());
|
|
35
38
|
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
|
|
36
39
|
log.error(LOG_PREFIX_SYNC_AUTH + 'non-retryable error fetching credential (status ' + error.statusCode + '): ' + error.message);
|
|
37
40
|
inFlightPromise = undefined;
|
|
@@ -46,18 +49,28 @@ export function authProviderFactory(settings, splitHttpClient, telemetryTracker)
|
|
|
46
49
|
if (cachedCredential && !isExpired(cachedCredential)) {
|
|
47
50
|
return Promise.resolve(cachedCredential);
|
|
48
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
|
+
}
|
|
49
59
|
if (cachedCredential)
|
|
50
60
|
log.debug(LOG_PREFIX_SYNC_AUTH + 'cached credential expired');
|
|
51
|
-
|
|
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();
|
|
52
64
|
},
|
|
53
65
|
invalidate: function () {
|
|
54
66
|
cachedCredential = undefined;
|
|
55
67
|
},
|
|
56
68
|
stop: function () {
|
|
57
69
|
stopped = true;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
backoff.
|
|
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() });
|
|
61
74
|
}
|
|
62
75
|
};
|
|
63
76
|
}
|
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
import { __assign } from "tslib";
|
|
2
|
-
import { splitHttpClientFactory } from './splitHttpClient';
|
|
3
|
-
import { authProviderFactory } from './authProvider';
|
|
4
2
|
/**
|
|
5
3
|
* Factory of Secure HTTP client, which authenticates requests using a JWT token.
|
|
6
4
|
* On 401 responses, invalidates the cached credential and retries once with a fresh token.
|
|
7
5
|
*
|
|
8
|
-
* @param
|
|
9
|
-
* @param
|
|
10
|
-
* @param fetchAuth - function to fetch auth credentials from the /v2/auth endpoint
|
|
6
|
+
* @param splitHttpClient - `splitHttpClientFactory` to use for making requests
|
|
7
|
+
* @param authProvider - `authProviderFactory` to use for authentication
|
|
11
8
|
*/
|
|
12
|
-
export function secureSplitHttpClientFactory(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function makeRequest(url, options, latencyTracker, logErrorsAsInfo, token) {
|
|
16
|
-
return splitHttpClient(url, __assign(__assign({}, options), { headers: __assign(__assign({}, options === null || options === void 0 ? void 0 : options.headers), { Authorization: "Bearer ".concat(token) }) }), latencyTracker, logErrorsAsInfo, true);
|
|
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);
|
|
17
12
|
}
|
|
18
|
-
var httpClient = function (url, options, latencyTracker, logErrorsAsInfo) {
|
|
13
|
+
var httpClient = function (url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt) {
|
|
14
|
+
if (newVersionHeader === void 0) { newVersionHeader = true; }
|
|
15
|
+
if (useJwt === void 0) { useJwt = true; }
|
|
19
16
|
return authProvider.credential().then(function (credential) {
|
|
20
|
-
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, credential.token)
|
|
17
|
+
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? credential.token : undefined)
|
|
21
18
|
.catch(function (error) {
|
|
22
19
|
if (error.statusCode === 401) {
|
|
23
20
|
// retry once for 401, in case the token has just expired
|
|
24
21
|
authProvider.invalidate();
|
|
25
22
|
return authProvider.credential().then(function (newCredential) {
|
|
26
|
-
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newCredential.token);
|
|
23
|
+
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newVersionHeader, useJwt ? newCredential.token : undefined);
|
|
27
24
|
});
|
|
28
25
|
}
|
|
29
26
|
throw error;
|