@splitsoftware/splitio-commons 3.1.1-rc.11 → 3.1.1-rc.13
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/services/authProvider.js +2 -2
- package/cjs/{sync/streaming/AuthClient/index.js → services/fetchAuth.js} +2 -2
- package/cjs/services/splitApi.js +2 -2
- package/cjs/storages/AbstractDefinitionsCacheAsync.js +2 -1
- package/cjs/storages/AbstractDefinitionsCacheSync.js +2 -2
- package/cjs/storages/inLocalStorage/DefinitionsCacheInLocal.js +4 -1
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +6 -3
- package/cjs/storages/inMemory/DefinitionsCacheInMemory.js +4 -1
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +6 -3
- package/cjs/storages/inRedis/DefinitionsCacheInRedis.js +12 -8
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +21 -11
- package/cjs/storages/pluggable/DefinitionsCachePluggable.js +12 -8
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +21 -11
- package/cjs/sync/offline/splitsParser/definitionChangesBuilder.js +37 -0
- package/cjs/sync/offline/splitsParser/splitsParserFromSettings.js +5 -32
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +26 -46
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +1 -1
- package/cjs/sync/polling/updaters/definitionChangesUpdater.js +22 -15
- package/cjs/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.js +6 -4
- package/cjs/sync/streaming/parseUtils.js +4 -4
- package/cjs/sync/streaming/pushManager.js +4 -3
- package/esm/services/authProvider.js +1 -1
- package/esm/{sync/streaming/AuthClient/index.js → services/fetchAuth.js} +2 -2
- package/esm/services/splitApi.js +1 -1
- package/esm/storages/AbstractDefinitionsCacheAsync.js +2 -1
- package/esm/storages/AbstractDefinitionsCacheSync.js +2 -2
- package/esm/storages/inLocalStorage/DefinitionsCacheInLocal.js +4 -1
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +6 -3
- package/esm/storages/inMemory/DefinitionsCacheInMemory.js +4 -1
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +6 -3
- package/esm/storages/inRedis/DefinitionsCacheInRedis.js +12 -8
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +21 -11
- package/esm/storages/pluggable/DefinitionsCachePluggable.js +12 -8
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +21 -11
- package/esm/sync/offline/splitsParser/definitionChangesBuilder.js +33 -0
- package/esm/sync/offline/splitsParser/splitsParserFromSettings.js +6 -33
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +23 -43
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +1 -1
- package/esm/sync/polling/updaters/definitionChangesUpdater.js +22 -15
- package/esm/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.js +8 -6
- package/esm/sync/streaming/parseUtils.js +2 -2
- package/esm/sync/streaming/pushManager.js +4 -3
- 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 +5 -4
- package/src/storages/AbstractDefinitionsCacheSync.ts +4 -4
- package/src/storages/inLocalStorage/DefinitionsCacheInLocal.ts +5 -3
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +7 -5
- package/src/storages/inMemory/DefinitionsCacheInMemory.ts +3 -2
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +5 -4
- package/src/storages/inRedis/DefinitionsCacheInRedis.ts +15 -11
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +26 -15
- package/src/storages/pluggable/DefinitionsCachePluggable.ts +14 -9
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +24 -13
- package/src/storages/types.ts +6 -6
- package/src/sync/offline/splitsParser/definitionChangesBuilder.ts +49 -0
- package/src/sync/offline/splitsParser/splitsParserFromSettings.ts +8 -38
- package/src/sync/offline/splitsParser/types.ts +7 -3
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +25 -45
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +5 -5
- package/src/sync/polling/types.ts +22 -1
- package/src/sync/polling/updaters/definitionChangesUpdater.ts +35 -33
- 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 +17 -13
- 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/src/sync/syncManagerOnline.ts +0 -1
- package/cjs/sync/streaming/AuthClient/types.js +0 -2
- package/esm/sync/streaming/AuthClient/types.js +0 -1
- package/src/sync/streaming/AuthClient/types.ts +0 -19
package/CHANGES.txt
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
3.2.0 (XXX XX, 2026)
|
|
2
|
+
- Added support for streaming in the Configs SDK, handling CONFIG_UPDATE notifications. Unlike feature flag notifications, the previous change number of CONFIG_UPDATE and RB_SEGMENT_UPDATE notifications are scoped to the referenced entity rather than to the environment, and so instant updates are applied without updating the storage change number.
|
|
2
3
|
- Added support for AI configs.
|
|
3
4
|
- Added support for ioredis v6 (Related to issue https://github.com/splitio/javascript-client/issues/951). Note that ioredis v6 requires Node.js 20 or above; stay on ioredis v4 or v5 for older Node.js versions.
|
|
4
5
|
- Removed the `@types/ioredis` dependency, which is no longer required since the public type definitions don't depend on ioredis types. The `@types/node` dependency, previously installed transitively by `@types/ioredis`, is now declared explicitly, since the public type definitions use Node.js types for the `agent` and `tls` options.
|
|
5
6
|
- Removed Configs SDK-related types and modules and moved them to the Configs SDK repository.
|
|
7
|
+
- Bugfix - Fixed the SDK_UPDATE event in localhost mode, which is now emitted with the correct list of names of the updated or removed feature flags.
|
|
8
|
+
- Bugfix - Fixed the SDK_UPDATE event emitted on SPLIT_KILL notifications, which now carries the metadata with the name of the killed feature flag (previously, the metadata was `true`). Also, SPLIT_KILL no longer triggers a second SDK_UPDATE event when the subsequent fetch returns the already killed feature flag with the same change number.
|
|
6
9
|
- Bugfix - Fixed a memory leak in the `Backoff` utility, where promises returned by pending scheduled calls were never settled when the backoff was reset.
|
|
7
10
|
- Bugfix - Fixed the authentication service to not send unauthenticated requests when the SDK is destroyed while an authentication retry is pending.
|
|
8
11
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.authProviderFactory = void 0;
|
|
4
|
-
var
|
|
4
|
+
var fetchAuth_1 = require("./fetchAuth");
|
|
5
5
|
var Backoff_1 = require("../utils/Backoff");
|
|
6
6
|
var constants_1 = require("../logger/constants");
|
|
7
7
|
var constants_2 = require("../utils/constants");
|
|
@@ -18,7 +18,7 @@ function noCachedCredentialError() {
|
|
|
18
18
|
*/
|
|
19
19
|
function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
|
|
20
20
|
var urls = settings.urls, log = settings.log;
|
|
21
|
-
var fetchAuth = (0,
|
|
21
|
+
var fetchAuth = (0, fetchAuth_1.fetchAuthFactory)(function () {
|
|
22
22
|
var url = "".concat(urls.auth, "/api/v3/auth?capabilities=config,aiconfig");
|
|
23
23
|
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(constants_2.TOKEN), false, true);
|
|
24
24
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.fetchAuthFactory = void 0;
|
|
4
|
-
var objectAssign_1 = require("
|
|
5
|
-
var jwt_1 = require("
|
|
4
|
+
var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
5
|
+
var jwt_1 = require("../utils/jwt");
|
|
6
6
|
/**
|
|
7
7
|
* Factory of authentication function.
|
|
8
8
|
*
|
package/cjs/services/splitApi.js
CHANGED
|
@@ -5,7 +5,7 @@ var splitHttpClient_1 = require("./splitHttpClient");
|
|
|
5
5
|
var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
6
6
|
var constants_1 = require("../utils/constants");
|
|
7
7
|
var constants_2 = require("../logger/constants");
|
|
8
|
-
var
|
|
8
|
+
var fetchAuth_1 = require("./fetchAuth");
|
|
9
9
|
var noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
|
|
10
10
|
function userKeyToQueryParam(userKey) {
|
|
11
11
|
return 'users=' + encodeURIComponent(userKey); // no need to check availability of `encodeURIComponent`, since it is a global highly supported.
|
|
@@ -24,7 +24,7 @@ function splitApiFactory(settings, platform, telemetryTracker) {
|
|
|
24
24
|
var SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
25
25
|
var splitHttpClient = (0, splitHttpClient_1.splitHttpClientFactory)(settings, platform);
|
|
26
26
|
return {
|
|
27
|
-
fetchAuth: (0,
|
|
27
|
+
fetchAuth: (0, fetchAuth_1.fetchAuthFactory)(function (userMatchingKeys) {
|
|
28
28
|
var url = "".concat(urls.auth, "/v2/auth?s=").concat(settings.sync.flagSpecVersion);
|
|
29
29
|
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
30
30
|
var queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
@@ -17,7 +17,8 @@ var AbstractDefinitionsCacheAsync = /** @class */ (function () {
|
|
|
17
17
|
Promise.all(toRemove.map(function (removedFF) { return _this.remove(removedFF); }))
|
|
18
18
|
]).then(function (_a) {
|
|
19
19
|
var added = _a[1], removed = _a[2];
|
|
20
|
-
return
|
|
20
|
+
return toRemove.filter(function (_, i) { return removed[i]; })
|
|
21
|
+
.concat(toAdd.filter(function (_, i) { return added[i]; }).map(function (addedFF) { return addedFF.name; }));
|
|
21
22
|
});
|
|
22
23
|
};
|
|
23
24
|
// @TODO revisit segment-related methods ('usesSegments', 'getRegisteredSegments', 'registerSegments')
|
|
@@ -12,8 +12,8 @@ var AbstractDefinitionsCacheSync = /** @class */ (function () {
|
|
|
12
12
|
}
|
|
13
13
|
AbstractDefinitionsCacheSync.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
14
14
|
var _this = this;
|
|
15
|
-
var updated =
|
|
16
|
-
|
|
15
|
+
var updated = toRemove.filter(function (removedFF) { return _this.remove(removedFF); })
|
|
16
|
+
.concat(toAdd.filter(function (addedFF) { return _this.add(addedFF); }).map(function (addedFF) { return addedFF.name; }));
|
|
17
17
|
this.setChangeNumber(changeNumber);
|
|
18
18
|
return updated;
|
|
19
19
|
};
|
|
@@ -75,6 +75,8 @@ var DefinitionsCacheInLocal = /** @class */ (function (_super) {
|
|
|
75
75
|
var definitionFromStorage = this.storage.getItem(definitionKey);
|
|
76
76
|
var previousDefinition = definitionFromStorage ? JSON.parse(definitionFromStorage) : null;
|
|
77
77
|
if (previousDefinition) {
|
|
78
|
+
if (previousDefinition.changeNumber >= definition.changeNumber)
|
|
79
|
+
return false;
|
|
78
80
|
this._decrementCounts(previousDefinition);
|
|
79
81
|
this.removeFromSets(previousDefinition.name, previousDefinition.sets);
|
|
80
82
|
}
|
|
@@ -98,7 +100,8 @@ var DefinitionsCacheInLocal = /** @class */ (function (_super) {
|
|
|
98
100
|
};
|
|
99
101
|
DefinitionsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
100
102
|
try {
|
|
101
|
-
|
|
103
|
+
if (changeNumber !== undefined)
|
|
104
|
+
this.storage.setItem(this.keys.buildDefinitionsTillKey(), changeNumber + '');
|
|
102
105
|
// update "last updated" timestamp with current time
|
|
103
106
|
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
104
107
|
this.hasSync = true;
|
|
@@ -18,14 +18,15 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
18
18
|
};
|
|
19
19
|
RBSegmentsCacheInLocal.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
20
20
|
var _this = this;
|
|
21
|
-
var updated =
|
|
22
|
-
|
|
21
|
+
var updated = toRemove.filter(function (name) { return _this.remove(name); })
|
|
22
|
+
.concat(toAdd.filter(function (rbSegment) { return _this.add(rbSegment); }).map(function (rbSegment) { return rbSegment.name; }));
|
|
23
23
|
this.setChangeNumber(changeNumber);
|
|
24
24
|
return updated;
|
|
25
25
|
};
|
|
26
26
|
RBSegmentsCacheInLocal.prototype.setChangeNumber = function (changeNumber) {
|
|
27
27
|
try {
|
|
28
|
-
|
|
28
|
+
if (changeNumber !== undefined)
|
|
29
|
+
this.storage.setItem(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
29
30
|
this.storage.setItem(this.keys.buildLastUpdatedKey(), Date.now() + '');
|
|
30
31
|
}
|
|
31
32
|
catch (e) {
|
|
@@ -45,6 +46,8 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
45
46
|
var rbSegmentKey = this.keys.buildRBSegmentKey(name);
|
|
46
47
|
var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
47
48
|
var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
49
|
+
if (previous && previous.changeNumber >= rbSegment.changeNumber)
|
|
50
|
+
return false;
|
|
48
51
|
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
49
52
|
var usesSegmentsDiff = 0;
|
|
50
53
|
if (previous && (0, AbstractDefinitionsCacheSync_1.usesSegments)(previous))
|
|
@@ -30,6 +30,8 @@ var DefinitionsCacheInMemory = /** @class */ (function (_super) {
|
|
|
30
30
|
var name = definition.name;
|
|
31
31
|
var previousDefinition = this.get(name);
|
|
32
32
|
if (previousDefinition) { // We had this Split already
|
|
33
|
+
if (previousDefinition.changeNumber >= definition.changeNumber)
|
|
34
|
+
return false;
|
|
33
35
|
var previousTtName = previousDefinition.trafficTypeName;
|
|
34
36
|
this.ttCache[previousTtName]--;
|
|
35
37
|
if (!this.ttCache[previousTtName])
|
|
@@ -70,7 +72,8 @@ var DefinitionsCacheInMemory = /** @class */ (function (_super) {
|
|
|
70
72
|
return this.definitionsCache[name] || null;
|
|
71
73
|
};
|
|
72
74
|
DefinitionsCacheInMemory.prototype.setChangeNumber = function (changeNumber) {
|
|
73
|
-
|
|
75
|
+
if (changeNumber !== undefined)
|
|
76
|
+
this.changeNumber = changeNumber;
|
|
74
77
|
return true;
|
|
75
78
|
};
|
|
76
79
|
DefinitionsCacheInMemory.prototype.getChangeNumber = function () {
|
|
@@ -16,14 +16,17 @@ var RBSegmentsCacheInMemory = /** @class */ (function () {
|
|
|
16
16
|
};
|
|
17
17
|
RBSegmentsCacheInMemory.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
18
18
|
var _this = this;
|
|
19
|
-
var updated =
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
var updated = toRemove.filter(function (name) { return _this.remove(name); })
|
|
20
|
+
.concat(toAdd.filter(function (rbSegment) { return _this.add(rbSegment); }).map(function (rbSegment) { return rbSegment.name; }));
|
|
21
|
+
if (changeNumber !== undefined)
|
|
22
|
+
this.changeNumber = changeNumber;
|
|
22
23
|
return updated;
|
|
23
24
|
};
|
|
24
25
|
RBSegmentsCacheInMemory.prototype.add = function (rbSegment) {
|
|
25
26
|
var name = rbSegment.name;
|
|
26
27
|
var previous = this.get(name);
|
|
28
|
+
if (previous && previous.changeNumber >= rbSegment.changeNumber)
|
|
29
|
+
return false;
|
|
27
30
|
if (previous && (0, AbstractDefinitionsCacheSync_1.usesSegments)(previous))
|
|
28
31
|
this.segmentsCount--;
|
|
29
32
|
this.cache[name] = rbSegment;
|
|
@@ -76,6 +76,8 @@ var DefinitionsCacheInRedis = /** @class */ (function (_super) {
|
|
|
76
76
|
var parsedPreviousDefinition, stringifiedNewDefinition;
|
|
77
77
|
try {
|
|
78
78
|
parsedPreviousDefinition = definitionFromStorage ? JSON.parse(definitionFromStorage) : undefined;
|
|
79
|
+
if (parsedPreviousDefinition && parsedPreviousDefinition.changeNumber >= definition.changeNumber)
|
|
80
|
+
return false;
|
|
79
81
|
stringifiedNewDefinition = JSON.stringify(definition);
|
|
80
82
|
}
|
|
81
83
|
catch (e) {
|
|
@@ -90,8 +92,8 @@ var DefinitionsCacheInRedis = /** @class */ (function (_super) {
|
|
|
90
92
|
if (parsedPreviousDefinition)
|
|
91
93
|
return _this._decrementCounts(parsedPreviousDefinition);
|
|
92
94
|
});
|
|
93
|
-
}).then(function () { return _this._updateSets(name, parsedPreviousDefinition && parsedPreviousDefinition.sets, definition.sets); });
|
|
94
|
-
})
|
|
95
|
+
}).then(function () { return _this._updateSets(name, parsedPreviousDefinition && parsedPreviousDefinition.sets, definition.sets); }).then(function () { return true; });
|
|
96
|
+
});
|
|
95
97
|
};
|
|
96
98
|
/**
|
|
97
99
|
* Remove a given definition.
|
|
@@ -101,11 +103,11 @@ var DefinitionsCacheInRedis = /** @class */ (function (_super) {
|
|
|
101
103
|
DefinitionsCacheInRedis.prototype.remove = function (name) {
|
|
102
104
|
var _this = this;
|
|
103
105
|
return this.get(name).then(function (definition) {
|
|
104
|
-
if (definition)
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
if (!definition)
|
|
107
|
+
return false;
|
|
108
|
+
return _this._decrementCounts(definition).then(function () { return _this._updateSets(name, definition.sets); }).then(function () {
|
|
109
|
+
return _this.redis.del(_this.keys.buildDefinitionKey(name)).then(function (status) { return status === 1; });
|
|
110
|
+
});
|
|
109
111
|
});
|
|
110
112
|
};
|
|
111
113
|
/**
|
|
@@ -126,7 +128,9 @@ var DefinitionsCacheInRedis = /** @class */ (function (_super) {
|
|
|
126
128
|
* or rejected if it fails.
|
|
127
129
|
*/
|
|
128
130
|
DefinitionsCacheInRedis.prototype.setChangeNumber = function (changeNumber) {
|
|
129
|
-
|
|
131
|
+
if (changeNumber !== undefined) {
|
|
132
|
+
return this.redis.set(this.keys.buildDefinitionsTillKey(), changeNumber + '').then(function (status) { return status === 'OK'; });
|
|
133
|
+
}
|
|
130
134
|
};
|
|
131
135
|
/**
|
|
132
136
|
* Get till number or -1 if it's not defined.
|
|
@@ -28,22 +28,32 @@ var RBSegmentsCacheInRedis = /** @class */ (function () {
|
|
|
28
28
|
var _this = this;
|
|
29
29
|
return Promise.all([
|
|
30
30
|
this.setChangeNumber(changeNumber),
|
|
31
|
-
Promise.all(toAdd.map(function (
|
|
32
|
-
|
|
33
|
-
var stringifiedNewRBSegment = JSON.stringify(toAdd);
|
|
34
|
-
return _this.redis.set(key, stringifiedNewRBSegment).then(function () { return true; });
|
|
35
|
-
})),
|
|
36
|
-
Promise.all(toRemove.map(function (toRemove) {
|
|
37
|
-
var key = _this.keys.buildRBSegmentKey(toRemove);
|
|
38
|
-
return _this.redis.del(key).then(function (status) { return status === 1; });
|
|
39
|
-
}))
|
|
31
|
+
Promise.all(toAdd.map(function (rbSegment) { return _this.add(rbSegment); })),
|
|
32
|
+
Promise.all(toRemove.map(function (name) { return _this.remove(name); }))
|
|
40
33
|
]).then(function (_a) {
|
|
41
34
|
var added = _a[1], removed = _a[2];
|
|
42
|
-
return
|
|
35
|
+
return toRemove.filter(function (_, i) { return removed[i]; })
|
|
36
|
+
.concat(toAdd.filter(function (_, i) { return added[i]; }).map(function (rbSegment) { return rbSegment.name; }));
|
|
43
37
|
});
|
|
44
38
|
};
|
|
39
|
+
RBSegmentsCacheInRedis.prototype.add = function (rbSegment) {
|
|
40
|
+
var _this = this;
|
|
41
|
+
var key = this.keys.buildRBSegmentKey(rbSegment.name);
|
|
42
|
+
return this.get(rbSegment.name).then(function (previous) {
|
|
43
|
+
if (previous && previous.changeNumber >= rbSegment.changeNumber)
|
|
44
|
+
return false;
|
|
45
|
+
var stringifiedNewRBSegment = JSON.stringify(rbSegment);
|
|
46
|
+
return _this.redis.set(key, stringifiedNewRBSegment).then(function () { return true; });
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
RBSegmentsCacheInRedis.prototype.remove = function (name) {
|
|
50
|
+
var key = this.keys.buildRBSegmentKey(name);
|
|
51
|
+
return this.redis.del(key).then(function (status) { return status === 1; });
|
|
52
|
+
};
|
|
45
53
|
RBSegmentsCacheInRedis.prototype.setChangeNumber = function (changeNumber) {
|
|
46
|
-
|
|
54
|
+
if (changeNumber !== undefined) {
|
|
55
|
+
return this.redis.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '').then(function (status) { return status === 'OK'; });
|
|
56
|
+
}
|
|
47
57
|
};
|
|
48
58
|
RBSegmentsCacheInRedis.prototype.getChangeNumber = function () {
|
|
49
59
|
var _this = this;
|
|
@@ -63,6 +63,8 @@ var DefinitionsCachePluggable = /** @class */ (function (_super) {
|
|
|
63
63
|
var parsedPreviousDefinition, stringifiedNewDefinition;
|
|
64
64
|
try {
|
|
65
65
|
parsedPreviousDefinition = definitionFromStorage ? JSON.parse(definitionFromStorage) : undefined;
|
|
66
|
+
if (parsedPreviousDefinition && parsedPreviousDefinition.changeNumber >= definition.changeNumber)
|
|
67
|
+
return false;
|
|
66
68
|
stringifiedNewDefinition = JSON.stringify(definition);
|
|
67
69
|
}
|
|
68
70
|
catch (e) {
|
|
@@ -77,8 +79,8 @@ var DefinitionsCachePluggable = /** @class */ (function (_super) {
|
|
|
77
79
|
if (parsedPreviousDefinition)
|
|
78
80
|
return _this._decrementCounts(parsedPreviousDefinition);
|
|
79
81
|
});
|
|
80
|
-
}).then(function () { return _this._updateSets(name, parsedPreviousDefinition && parsedPreviousDefinition.sets, definition.sets); });
|
|
81
|
-
})
|
|
82
|
+
}).then(function () { return _this._updateSets(name, parsedPreviousDefinition && parsedPreviousDefinition.sets, definition.sets); }).then(function () { return true; });
|
|
83
|
+
});
|
|
82
84
|
};
|
|
83
85
|
/**
|
|
84
86
|
* Remove a given definition.
|
|
@@ -88,11 +90,11 @@ var DefinitionsCachePluggable = /** @class */ (function (_super) {
|
|
|
88
90
|
DefinitionsCachePluggable.prototype.remove = function (name) {
|
|
89
91
|
var _this = this;
|
|
90
92
|
return this.get(name).then(function (definition) {
|
|
91
|
-
if (definition)
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
if (!definition)
|
|
94
|
+
return false;
|
|
95
|
+
return _this._decrementCounts(definition).then(function () { return _this._updateSets(name, definition.sets); }).then(function () {
|
|
96
|
+
return _this.wrapper.del(_this.keys.buildDefinitionKey(name));
|
|
97
|
+
});
|
|
96
98
|
});
|
|
97
99
|
};
|
|
98
100
|
/**
|
|
@@ -186,7 +188,9 @@ var DefinitionsCachePluggable = /** @class */ (function (_super) {
|
|
|
186
188
|
* or rejected if it fails (e.g., wrapper operation fails).
|
|
187
189
|
*/
|
|
188
190
|
DefinitionsCachePluggable.prototype.setChangeNumber = function (changeNumber) {
|
|
189
|
-
|
|
191
|
+
if (changeNumber !== undefined) {
|
|
192
|
+
return this.wrapper.set(this.keys.buildDefinitionsTillKey(), changeNumber + '');
|
|
193
|
+
}
|
|
190
194
|
};
|
|
191
195
|
/**
|
|
192
196
|
* Get till number or -1 if it's not defined.
|
|
@@ -28,22 +28,32 @@ var RBSegmentsCachePluggable = /** @class */ (function () {
|
|
|
28
28
|
var _this = this;
|
|
29
29
|
return Promise.all([
|
|
30
30
|
this.setChangeNumber(changeNumber),
|
|
31
|
-
Promise.all(toAdd.map(function (
|
|
32
|
-
|
|
33
|
-
var stringifiedNewRBSegment = JSON.stringify(toAdd);
|
|
34
|
-
return _this.wrapper.set(key, stringifiedNewRBSegment).then(function () { return true; });
|
|
35
|
-
})),
|
|
36
|
-
Promise.all(toRemove.map(function (toRemove) {
|
|
37
|
-
var key = _this.keys.buildRBSegmentKey(toRemove);
|
|
38
|
-
return _this.wrapper.del(key);
|
|
39
|
-
}))
|
|
31
|
+
Promise.all(toAdd.map(function (rbSegment) { return _this.add(rbSegment); })),
|
|
32
|
+
Promise.all(toRemove.map(function (name) { return _this.remove(name); }))
|
|
40
33
|
]).then(function (_a) {
|
|
41
34
|
var added = _a[1], removed = _a[2];
|
|
42
|
-
return
|
|
35
|
+
return toRemove.filter(function (_, i) { return removed[i]; })
|
|
36
|
+
.concat(toAdd.filter(function (_, i) { return added[i]; }).map(function (rbSegment) { return rbSegment.name; }));
|
|
43
37
|
});
|
|
44
38
|
};
|
|
39
|
+
RBSegmentsCachePluggable.prototype.add = function (rbSegment) {
|
|
40
|
+
var _this = this;
|
|
41
|
+
var key = this.keys.buildRBSegmentKey(rbSegment.name);
|
|
42
|
+
return this.get(rbSegment.name).then(function (previous) {
|
|
43
|
+
if (previous && previous.changeNumber >= rbSegment.changeNumber)
|
|
44
|
+
return false;
|
|
45
|
+
var stringifiedNewRBSegment = JSON.stringify(rbSegment);
|
|
46
|
+
return _this.wrapper.set(key, stringifiedNewRBSegment).then(function () { return true; });
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
RBSegmentsCachePluggable.prototype.remove = function (name) {
|
|
50
|
+
var key = this.keys.buildRBSegmentKey(name);
|
|
51
|
+
return this.wrapper.del(key);
|
|
52
|
+
};
|
|
45
53
|
RBSegmentsCachePluggable.prototype.setChangeNumber = function (changeNumber) {
|
|
46
|
-
|
|
54
|
+
if (changeNumber !== undefined) {
|
|
55
|
+
return this.wrapper.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
56
|
+
}
|
|
47
57
|
};
|
|
48
58
|
RBSegmentsCachePluggable.prototype.getChangeNumber = function () {
|
|
49
59
|
var _this = this;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.definitionChangesBuilderFactory = void 0;
|
|
4
|
+
var tslib_1 = require("tslib");
|
|
5
|
+
var constants_1 = require("../../../utils/constants");
|
|
6
|
+
/**
|
|
7
|
+
* Factory of a stateful function that, given the current mocked definitions, returns the changes since the previous call
|
|
8
|
+
* as an `IDefinitionChangesResponse`, i.e., the full definitions that were added or modified, and the names of the removed ones.
|
|
9
|
+
* Definitions are compared by value. The response `till` is an incrementing value based on the current time.
|
|
10
|
+
* Definitions have no `changeNumber`, so storages never consider them outdated.
|
|
11
|
+
*
|
|
12
|
+
* The response has no `d` property if there are no changes, except for the first call, which reports the (possibly empty) mock.
|
|
13
|
+
* It is meant to be used by `IDefinitionsParser` implementations, such as `splitsParserFromSettings`.
|
|
14
|
+
*/
|
|
15
|
+
function definitionChangesBuilderFactory() {
|
|
16
|
+
var previous; // serialized definitions of the last reported mock, by name
|
|
17
|
+
var lastTill = 0;
|
|
18
|
+
return function definitionChangesBuilder(mock) {
|
|
19
|
+
var current = {};
|
|
20
|
+
var updatedNames = Object.keys(mock).reduce(function (updatedNames, name) {
|
|
21
|
+
current[name] = JSON.stringify(mock[name]);
|
|
22
|
+
if (!previous || previous[name] !== current[name])
|
|
23
|
+
updatedNames.push(name);
|
|
24
|
+
return updatedNames;
|
|
25
|
+
}, []);
|
|
26
|
+
var removed = previous ? Object.keys(previous).filter(function (name) { return current[name] === undefined; }) : [];
|
|
27
|
+
// Only the first call reports an empty change
|
|
28
|
+
if (previous && updatedNames.length === 0 && removed.length === 0)
|
|
29
|
+
return {};
|
|
30
|
+
previous = current;
|
|
31
|
+
// Incrementing `till` based on the current time
|
|
32
|
+
var till = lastTill = Math.max(Date.now(), lastTill + 1);
|
|
33
|
+
var updated = updatedNames.map(function (name) { return (tslib_1.__assign({ name: name, status: 'ACTIVE', killed: false, trafficAllocation: 100, defaultTreatment: constants_1.CONTROL }, mock[name])); });
|
|
34
|
+
return { d: { updated: updated, removed: removed, till: till } };
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
exports.definitionChangesBuilderFactory = definitionChangesBuilderFactory;
|
|
@@ -3,43 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.splitsParserFromSettingsFactory = void 0;
|
|
4
4
|
var lang_1 = require("../../../utils/lang");
|
|
5
5
|
var parseCondition_1 = require("./parseCondition");
|
|
6
|
-
|
|
7
|
-
if (typeof prev !== typeof curr)
|
|
8
|
-
return true;
|
|
9
|
-
if (typeof prev === 'string') { // strings treatments, just compare
|
|
10
|
-
return prev !== curr;
|
|
11
|
-
}
|
|
12
|
-
else { // has treatment and config, compare both
|
|
13
|
-
return prev.treatment !== curr.treatment || prev.config !== curr.config;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
6
|
+
var definitionChangesBuilder_1 = require("./definitionChangesBuilder");
|
|
16
7
|
function splitsParserFromSettingsFactory() {
|
|
17
|
-
var
|
|
18
|
-
function mockUpdated(currentData) {
|
|
19
|
-
var names = Object.keys(currentData);
|
|
20
|
-
// Different amount of items
|
|
21
|
-
if (names.length !== Object.keys(previousMock).length) {
|
|
22
|
-
previousMock = (0, lang_1.merge)({}, currentData);
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
return names.some(function (name) {
|
|
26
|
-
var newSplit = !previousMock[name];
|
|
27
|
-
var newTreatment = hasTreatmentChanged(previousMock[name], currentData[name]);
|
|
28
|
-
var changed = newSplit || newTreatment;
|
|
29
|
-
if (changed)
|
|
30
|
-
previousMock = (0, lang_1.merge)({}, currentData);
|
|
31
|
-
return changed;
|
|
32
|
-
});
|
|
33
|
-
}
|
|
8
|
+
var definitionChangesBuilder = (0, definitionChangesBuilder_1.definitionChangesBuilderFactory)();
|
|
34
9
|
/**
|
|
35
10
|
*
|
|
36
11
|
* @param settings - validated object with mocked features mapping.
|
|
37
12
|
*/
|
|
38
13
|
return function splitsParserFromSettings(settings) {
|
|
39
14
|
var features = settings.features || {};
|
|
40
|
-
|
|
41
|
-
return false;
|
|
42
|
-
var splitObjects = {};
|
|
15
|
+
var mock = {};
|
|
43
16
|
(0, lang_1.forOwn)(features, function (data, splitName) {
|
|
44
17
|
var treatment = data;
|
|
45
18
|
var config = null;
|
|
@@ -50,13 +23,13 @@ function splitsParserFromSettingsFactory() {
|
|
|
50
23
|
var configurations = {};
|
|
51
24
|
if (config !== null)
|
|
52
25
|
configurations[treatment] = config;
|
|
53
|
-
|
|
26
|
+
mock[splitName] = {
|
|
54
27
|
trafficTypeName: 'localhost',
|
|
55
28
|
conditions: [(0, parseCondition_1.parseCondition)({ treatment: treatment })],
|
|
56
29
|
configurations: configurations
|
|
57
30
|
};
|
|
58
31
|
});
|
|
59
|
-
return
|
|
32
|
+
return definitionChangesBuilder(mock);
|
|
60
33
|
};
|
|
61
34
|
}
|
|
62
35
|
exports.splitsParserFromSettingsFactory = splitsParserFromSettingsFactory;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.fromObjectSyncTaskFactory = exports.fromObjectUpdaterFactory = void 0;
|
|
4
|
-
var lang_1 = require("../../../utils/lang");
|
|
5
4
|
var syncTask_1 = require("../../syncTask");
|
|
6
|
-
var constants_1 = require("../../../
|
|
7
|
-
var constants_2 = require("../../../
|
|
8
|
-
var constants_3 = require("../../../logger/constants");
|
|
5
|
+
var constants_1 = require("../../../readiness/constants");
|
|
6
|
+
var constants_2 = require("../../../logger/constants");
|
|
9
7
|
/**
|
|
10
8
|
* Offline equivalent of `splitChangesUpdaterFactory`
|
|
11
9
|
*/
|
|
@@ -13,53 +11,35 @@ function fromObjectUpdaterFactory(splitsParser, storage, readiness, settings) {
|
|
|
13
11
|
var log = settings.log, definitions = storage.definitions;
|
|
14
12
|
var startingUp = true;
|
|
15
13
|
return function objectUpdater() {
|
|
16
|
-
var
|
|
17
|
-
var loadError = null;
|
|
18
|
-
var splitsMock = {};
|
|
14
|
+
var changes;
|
|
19
15
|
try {
|
|
20
|
-
|
|
16
|
+
changes = splitsParser(settings);
|
|
21
17
|
}
|
|
22
18
|
catch (err) {
|
|
23
|
-
|
|
24
|
-
log.error(constants_3.ERROR_SYNC_OFFLINE_LOADING, [err]);
|
|
19
|
+
log.error(constants_2.ERROR_SYNC_OFFLINE_LOADING, [err]);
|
|
25
20
|
}
|
|
26
|
-
if (!
|
|
27
|
-
log.debug(constants_3.SYNC_OFFLINE_DATA, [JSON.stringify(splitsMock)]);
|
|
28
|
-
(0, lang_1.forOwn)(splitsMock, function (val, name) {
|
|
29
|
-
// @ts-ignore Split changeNumber and seed is undefined in localhost mode
|
|
30
|
-
splits.push({
|
|
31
|
-
name: name,
|
|
32
|
-
status: 'ACTIVE',
|
|
33
|
-
killed: false,
|
|
34
|
-
trafficAllocation: 100,
|
|
35
|
-
defaultTreatment: constants_1.CONTROL,
|
|
36
|
-
conditions: val.conditions || [],
|
|
37
|
-
configurations: val.configurations,
|
|
38
|
-
trafficTypeName: val.trafficTypeName
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
return Promise.all([
|
|
42
|
-
definitions.clear(),
|
|
43
|
-
definitions.update(splits, [], Date.now())
|
|
44
|
-
]).then(function () {
|
|
45
|
-
readiness.definitions.emit(constants_2.SDK_DEFINITIONS_ARRIVED, { type: constants_2.FLAGS_UPDATE, names: [] });
|
|
46
|
-
if (startingUp) {
|
|
47
|
-
startingUp = false;
|
|
48
|
-
Promise.resolve(storage.validateCache ? storage.validateCache() : { initialCacheLoad: true /* Fallback: assume initial load when validateCache doesn't exist */ }).then(function (cacheMetadata) {
|
|
49
|
-
// Emits SDK_READY_FROM_CACHE
|
|
50
|
-
if (!cacheMetadata.initialCacheLoad) {
|
|
51
|
-
readiness.definitions.emit(constants_2.SDK_DEFINITIONS_CACHE_LOADED, cacheMetadata);
|
|
52
|
-
}
|
|
53
|
-
// Emits SDK_READY
|
|
54
|
-
readiness.segments.emit(constants_2.SDK_SEGMENTS_ARRIVED, { type: constants_2.SEGMENTS_UPDATE, names: [] });
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
return true;
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
21
|
+
if (!changes || !changes.d)
|
|
61
22
|
return Promise.resolve(true);
|
|
62
|
-
|
|
23
|
+
var _a = changes.d, updated = _a.updated, removed = _a.removed, till = _a.till;
|
|
24
|
+
log.debug(constants_2.SYNC_OFFLINE_DATA, [JSON.stringify(changes.d)]);
|
|
25
|
+
// On start, clear definitions that might be in the storage from a previous session (InLocalStorage), since the parser only tracks its own changes.
|
|
26
|
+
return Promise.resolve(startingUp && definitions.clear()).then(function () {
|
|
27
|
+
return definitions.update(updated, removed, till);
|
|
28
|
+
}).then(function (names) {
|
|
29
|
+
readiness.definitions.emit(constants_1.SDK_DEFINITIONS_ARRIVED, { type: constants_1.FLAGS_UPDATE, names: names });
|
|
30
|
+
if (startingUp) {
|
|
31
|
+
startingUp = false;
|
|
32
|
+
Promise.resolve(storage.validateCache ? storage.validateCache() : { initialCacheLoad: true /* Fallback: assume initial load when validateCache doesn't exist */ }).then(function (cacheMetadata) {
|
|
33
|
+
// Emits SDK_READY_FROM_CACHE
|
|
34
|
+
if (!cacheMetadata.initialCacheLoad) {
|
|
35
|
+
readiness.definitions.emit(constants_1.SDK_DEFINITIONS_CACHE_LOADED, cacheMetadata);
|
|
36
|
+
}
|
|
37
|
+
// Emits SDK_READY
|
|
38
|
+
readiness.segments.emit(constants_1.SDK_SEGMENTS_ARRIVED, { type: constants_1.SEGMENTS_UPDATE, names: [] });
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
});
|
|
63
43
|
};
|
|
64
44
|
}
|
|
65
45
|
exports.fromObjectUpdaterFactory = fromObjectUpdaterFactory;
|
|
@@ -16,7 +16,7 @@ function isSplitChangesLegacyResponse(data) {
|
|
|
16
16
|
function partitionByStatus(items, till, since) {
|
|
17
17
|
var updated = [], removed = [];
|
|
18
18
|
items.forEach(function (item) { return item.status === 'ARCHIVED' ? removed.push(item.name) : updated.push(item); });
|
|
19
|
-
return { updated: updated, removed: removed, till: till, since: since };
|
|
19
|
+
return { updated: updated, removed: removed, till: till, since: since !== null && since !== void 0 ? since : undefined };
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Factory of SplitChanges fetcher.
|