@splitsoftware/splitio-commons 3.1.1-rc.12 → 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 +2 -0
- package/cjs/storages/AbstractDefinitionsCacheAsync.js +2 -1
- package/cjs/storages/AbstractDefinitionsCacheSync.js +2 -2
- package/cjs/storages/inLocalStorage/DefinitionsCacheInLocal.js +2 -0
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +4 -2
- package/cjs/storages/inMemory/DefinitionsCacheInMemory.js +2 -0
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +4 -2
- package/cjs/storages/inRedis/DefinitionsCacheInRedis.js +9 -7
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +18 -10
- package/cjs/storages/pluggable/DefinitionsCachePluggable.js +9 -7
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +18 -10
- 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/updaters/definitionChangesUpdater.js +8 -11
- package/cjs/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.js +1 -1
- package/esm/storages/AbstractDefinitionsCacheAsync.js +2 -1
- package/esm/storages/AbstractDefinitionsCacheSync.js +2 -2
- package/esm/storages/inLocalStorage/DefinitionsCacheInLocal.js +2 -0
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +4 -2
- package/esm/storages/inMemory/DefinitionsCacheInMemory.js +2 -0
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +4 -2
- package/esm/storages/inRedis/DefinitionsCacheInRedis.js +9 -7
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +18 -10
- package/esm/storages/pluggable/DefinitionsCachePluggable.js +9 -7
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +18 -10
- 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/updaters/definitionChangesUpdater.js +8 -11
- package/esm/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.js +2 -2
- package/package.json +1 -1
- package/src/storages/AbstractDefinitionsCacheAsync.ts +3 -2
- package/src/storages/AbstractDefinitionsCacheSync.ts +3 -3
- package/src/storages/inLocalStorage/DefinitionsCacheInLocal.ts +3 -1
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +5 -3
- package/src/storages/inMemory/DefinitionsCacheInMemory.ts +1 -0
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +4 -3
- package/src/storages/inRedis/DefinitionsCacheInRedis.ts +9 -7
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +20 -11
- package/src/storages/pluggable/DefinitionsCachePluggable.ts +10 -7
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +20 -11
- 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/updaters/definitionChangesUpdater.ts +9 -13
- package/src/sync/streaming/UpdateWorkers/DefinitionsUpdateWorker.ts +2 -2
- package/src/sync/syncManagerOnline.ts +0 -1
package/CHANGES.txt
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
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.
|
|
5
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.
|
|
6
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.
|
|
7
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.
|
|
8
10
|
- Bugfix - Fixed the authentication service to not send unauthenticated requests when the SDK is destroyed while an authentication retry is pending.
|
|
9
11
|
|
|
@@ -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
|
}
|
|
@@ -18,8 +18,8 @@ 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
|
};
|
|
@@ -46,6 +46,8 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
46
46
|
var rbSegmentKey = this.keys.buildRBSegmentKey(name);
|
|
47
47
|
var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
48
48
|
var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
49
|
+
if (previous && previous.changeNumber >= rbSegment.changeNumber)
|
|
50
|
+
return false;
|
|
49
51
|
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
50
52
|
var usesSegmentsDiff = 0;
|
|
51
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])
|
|
@@ -16,8 +16,8 @@ var RBSegmentsCacheInMemory = /** @class */ (function () {
|
|
|
16
16
|
};
|
|
17
17
|
RBSegmentsCacheInMemory.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
18
18
|
var _this = this;
|
|
19
|
-
var updated =
|
|
20
|
-
|
|
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
21
|
if (changeNumber !== undefined)
|
|
22
22
|
this.changeNumber = changeNumber;
|
|
23
23
|
return updated;
|
|
@@ -25,6 +25,8 @@ var RBSegmentsCacheInMemory = /** @class */ (function () {
|
|
|
25
25
|
RBSegmentsCacheInMemory.prototype.add = function (rbSegment) {
|
|
26
26
|
var name = rbSegment.name;
|
|
27
27
|
var previous = this.get(name);
|
|
28
|
+
if (previous && previous.changeNumber >= rbSegment.changeNumber)
|
|
29
|
+
return false;
|
|
28
30
|
if (previous && (0, AbstractDefinitionsCacheSync_1.usesSegments)(previous))
|
|
29
31
|
this.segmentsCount--;
|
|
30
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
|
/**
|
|
@@ -28,20 +28,28 @@ 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) {
|
|
47
55
|
return this.redis.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '').then(function (status) { return status === 'OK'; });
|
|
@@ -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
|
/**
|
|
@@ -28,20 +28,28 @@ 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) {
|
|
47
55
|
return this.wrapper.set(this.keys.buildRBSegmentsTillKey(), changeNumber + '');
|
|
@@ -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;
|
|
@@ -72,9 +72,8 @@ function computeMutation(update, segments, filters) {
|
|
|
72
72
|
else {
|
|
73
73
|
accum.removed.push(ruleEntity.name);
|
|
74
74
|
}
|
|
75
|
-
accum.names.push(ruleEntity.name);
|
|
76
75
|
return accum;
|
|
77
|
-
}, { updated: [], removed: update.removed
|
|
76
|
+
}, { updated: [], removed: update.removed });
|
|
78
77
|
}
|
|
79
78
|
exports.computeMutation = computeMutation;
|
|
80
79
|
function convertInstantUpdateToDefinitionChanges(instantUpdate) {
|
|
@@ -147,30 +146,28 @@ function definitionChangesUpdaterFactory(log, definitionChangesFetcher, storage,
|
|
|
147
146
|
.then(function (definitionChanges) {
|
|
148
147
|
var usedSegments = new Set();
|
|
149
148
|
var updatedDefinitions = [];
|
|
150
|
-
var ffUpdate = false;
|
|
151
149
|
if (definitionChanges.d) {
|
|
152
|
-
var _a = computeMutation(definitionChanges.d, usedSegments, splitFiltersValidation), updated = _a.updated, removed = _a.removed
|
|
153
|
-
updatedDefinitions = names;
|
|
150
|
+
var _a = computeMutation(definitionChanges.d, usedSegments, splitFiltersValidation), updated = _a.updated, removed = _a.removed;
|
|
154
151
|
log.debug(constants_2.SYNC_UPDATE, [definitionChangesFetcher.type, updated.length, removed.length]);
|
|
155
|
-
|
|
152
|
+
updatedDefinitions = definitions.update(updated, removed, definitionChanges.d.till);
|
|
156
153
|
}
|
|
157
|
-
var
|
|
154
|
+
var updatedRBSNames = [];
|
|
158
155
|
if (definitionChanges.rbs) {
|
|
159
156
|
var _b = computeMutation(definitionChanges.rbs, usedSegments), updated = _b.updated, removed = _b.removed;
|
|
160
157
|
log.debug(constants_2.SYNC_UPDATE, ['rule-based segments', updated.length, removed.length]);
|
|
161
|
-
|
|
158
|
+
updatedRBSNames = rbSegments.update(updated, removed, definitionChanges.rbs.till);
|
|
162
159
|
}
|
|
163
|
-
return Promise.all([
|
|
160
|
+
return Promise.all([updatedDefinitions, updatedRBSNames,
|
|
164
161
|
// @TODO if at least 1 segment fetch fails due to 404 and other segments are updated in the storage, SDK_UPDATE is not emitted
|
|
165
162
|
segments.registerSegments((0, sets_1.setToArray)(usedSegments))
|
|
166
163
|
]).then(function (_a) {
|
|
167
|
-
var
|
|
164
|
+
var updatedDefinitions = _a[0], updatedRBSNames = _a[1];
|
|
168
165
|
if (storage.save)
|
|
169
166
|
storage.save();
|
|
170
167
|
startingUp = false;
|
|
171
168
|
if (definitionsEventEmitter) {
|
|
172
169
|
// To emit SDK_DEFINITIONS_ARRIVED for server-side SDK, we must wait for all registered segments to be fetched
|
|
173
|
-
return Promise.resolve(!definitionsEventEmitter.definitionsArrived || ((
|
|
170
|
+
return Promise.resolve(!definitionsEventEmitter.definitionsArrived || ((updatedDefinitions.length > 0 || updatedRBSNames.length > 0) && (!segmentsSyncTask || segmentsSyncTask.execute(true))))
|
|
174
171
|
.catch(function () { return false; } /** noop. just to handle a possible `checkAllSegmentsExist` rejection, before emitting SDK event */)
|
|
175
172
|
.then(function (emitSplitsArrivedEvent) {
|
|
176
173
|
// emit SDK events
|
|
@@ -121,7 +121,7 @@ function DefinitionsUpdateWorker(params, pollingManager) {
|
|
|
121
121
|
var changeNumber = _a.changeNumber, splitName = _a.splitName, defaultTreatment = _a.defaultTreatment;
|
|
122
122
|
if (storage.definitions.killLocally(splitName, defaultTreatment, changeNumber)) {
|
|
123
123
|
// trigger an SDK_UPDATE if Split was killed locally
|
|
124
|
-
definitionsEventEmitter.emit(constants_2.SDK_DEFINITIONS_ARRIVED, true);
|
|
124
|
+
definitionsEventEmitter.emit(constants_2.SDK_DEFINITIONS_ARRIVED, { type: constants_2.FLAGS_UPDATE, names: [splitName] }, true /* isSplitKill */);
|
|
125
125
|
}
|
|
126
126
|
// queues the SplitChanges fetch (only if changeNumber is newer)
|
|
127
127
|
ff.put({ changeNumber: changeNumber });
|
|
@@ -14,7 +14,8 @@ var AbstractDefinitionsCacheAsync = /** @class */ (function () {
|
|
|
14
14
|
Promise.all(toRemove.map(function (removedFF) { return _this.remove(removedFF); }))
|
|
15
15
|
]).then(function (_a) {
|
|
16
16
|
var added = _a[1], removed = _a[2];
|
|
17
|
-
return
|
|
17
|
+
return toRemove.filter(function (_, i) { return removed[i]; })
|
|
18
|
+
.concat(toAdd.filter(function (_, i) { return added[i]; }).map(function (addedFF) { return addedFF.name; }));
|
|
18
19
|
});
|
|
19
20
|
};
|
|
20
21
|
// @TODO revisit segment-related methods ('usesSegments', 'getRegisteredSegments', 'registerSegments')
|
|
@@ -9,8 +9,8 @@ var AbstractDefinitionsCacheSync = /** @class */ (function () {
|
|
|
9
9
|
}
|
|
10
10
|
AbstractDefinitionsCacheSync.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
11
11
|
var _this = this;
|
|
12
|
-
var updated =
|
|
13
|
-
|
|
12
|
+
var updated = toRemove.filter(function (removedFF) { return _this.remove(removedFF); })
|
|
13
|
+
.concat(toAdd.filter(function (addedFF) { return _this.add(addedFF); }).map(function (addedFF) { return addedFF.name; }));
|
|
14
14
|
this.setChangeNumber(changeNumber);
|
|
15
15
|
return updated;
|
|
16
16
|
};
|
|
@@ -72,6 +72,8 @@ var DefinitionsCacheInLocal = /** @class */ (function (_super) {
|
|
|
72
72
|
var definitionFromStorage = this.storage.getItem(definitionKey);
|
|
73
73
|
var previousDefinition = definitionFromStorage ? JSON.parse(definitionFromStorage) : null;
|
|
74
74
|
if (previousDefinition) {
|
|
75
|
+
if (previousDefinition.changeNumber >= definition.changeNumber)
|
|
76
|
+
return false;
|
|
75
77
|
this._decrementCounts(previousDefinition);
|
|
76
78
|
this.removeFromSets(previousDefinition.name, previousDefinition.sets);
|
|
77
79
|
}
|
|
@@ -15,8 +15,8 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
15
15
|
};
|
|
16
16
|
RBSegmentsCacheInLocal.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
17
17
|
var _this = this;
|
|
18
|
-
var updated =
|
|
19
|
-
|
|
18
|
+
var updated = toRemove.filter(function (name) { return _this.remove(name); })
|
|
19
|
+
.concat(toAdd.filter(function (rbSegment) { return _this.add(rbSegment); }).map(function (rbSegment) { return rbSegment.name; }));
|
|
20
20
|
this.setChangeNumber(changeNumber);
|
|
21
21
|
return updated;
|
|
22
22
|
};
|
|
@@ -43,6 +43,8 @@ var RBSegmentsCacheInLocal = /** @class */ (function () {
|
|
|
43
43
|
var rbSegmentKey = this.keys.buildRBSegmentKey(name);
|
|
44
44
|
var rbSegmentFromStorage = this.storage.getItem(rbSegmentKey);
|
|
45
45
|
var previous = rbSegmentFromStorage ? JSON.parse(rbSegmentFromStorage) : null;
|
|
46
|
+
if (previous && previous.changeNumber >= rbSegment.changeNumber)
|
|
47
|
+
return false;
|
|
46
48
|
this.storage.setItem(rbSegmentKey, JSON.stringify(rbSegment));
|
|
47
49
|
var usesSegmentsDiff = 0;
|
|
48
50
|
if (previous && usesSegments(previous))
|
|
@@ -27,6 +27,8 @@ var DefinitionsCacheInMemory = /** @class */ (function (_super) {
|
|
|
27
27
|
var name = definition.name;
|
|
28
28
|
var previousDefinition = this.get(name);
|
|
29
29
|
if (previousDefinition) { // We had this Split already
|
|
30
|
+
if (previousDefinition.changeNumber >= definition.changeNumber)
|
|
31
|
+
return false;
|
|
30
32
|
var previousTtName = previousDefinition.trafficTypeName;
|
|
31
33
|
this.ttCache[previousTtName]--;
|
|
32
34
|
if (!this.ttCache[previousTtName])
|
|
@@ -13,8 +13,8 @@ var RBSegmentsCacheInMemory = /** @class */ (function () {
|
|
|
13
13
|
};
|
|
14
14
|
RBSegmentsCacheInMemory.prototype.update = function (toAdd, toRemove, changeNumber) {
|
|
15
15
|
var _this = this;
|
|
16
|
-
var updated =
|
|
17
|
-
|
|
16
|
+
var updated = toRemove.filter(function (name) { return _this.remove(name); })
|
|
17
|
+
.concat(toAdd.filter(function (rbSegment) { return _this.add(rbSegment); }).map(function (rbSegment) { return rbSegment.name; }));
|
|
18
18
|
if (changeNumber !== undefined)
|
|
19
19
|
this.changeNumber = changeNumber;
|
|
20
20
|
return updated;
|
|
@@ -22,6 +22,8 @@ var RBSegmentsCacheInMemory = /** @class */ (function () {
|
|
|
22
22
|
RBSegmentsCacheInMemory.prototype.add = function (rbSegment) {
|
|
23
23
|
var name = rbSegment.name;
|
|
24
24
|
var previous = this.get(name);
|
|
25
|
+
if (previous && previous.changeNumber >= rbSegment.changeNumber)
|
|
26
|
+
return false;
|
|
25
27
|
if (previous && usesSegments(previous))
|
|
26
28
|
this.segmentsCount--;
|
|
27
29
|
this.cache[name] = rbSegment;
|
|
@@ -73,6 +73,8 @@ var DefinitionsCacheInRedis = /** @class */ (function (_super) {
|
|
|
73
73
|
var parsedPreviousDefinition, stringifiedNewDefinition;
|
|
74
74
|
try {
|
|
75
75
|
parsedPreviousDefinition = definitionFromStorage ? JSON.parse(definitionFromStorage) : undefined;
|
|
76
|
+
if (parsedPreviousDefinition && parsedPreviousDefinition.changeNumber >= definition.changeNumber)
|
|
77
|
+
return false;
|
|
76
78
|
stringifiedNewDefinition = JSON.stringify(definition);
|
|
77
79
|
}
|
|
78
80
|
catch (e) {
|
|
@@ -87,8 +89,8 @@ var DefinitionsCacheInRedis = /** @class */ (function (_super) {
|
|
|
87
89
|
if (parsedPreviousDefinition)
|
|
88
90
|
return _this._decrementCounts(parsedPreviousDefinition);
|
|
89
91
|
});
|
|
90
|
-
}).then(function () { return _this._updateSets(name, parsedPreviousDefinition && parsedPreviousDefinition.sets, definition.sets); });
|
|
91
|
-
})
|
|
92
|
+
}).then(function () { return _this._updateSets(name, parsedPreviousDefinition && parsedPreviousDefinition.sets, definition.sets); }).then(function () { return true; });
|
|
93
|
+
});
|
|
92
94
|
};
|
|
93
95
|
/**
|
|
94
96
|
* Remove a given definition.
|
|
@@ -98,11 +100,11 @@ var DefinitionsCacheInRedis = /** @class */ (function (_super) {
|
|
|
98
100
|
DefinitionsCacheInRedis.prototype.remove = function (name) {
|
|
99
101
|
var _this = this;
|
|
100
102
|
return this.get(name).then(function (definition) {
|
|
101
|
-
if (definition)
|
|
102
|
-
return
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
if (!definition)
|
|
104
|
+
return false;
|
|
105
|
+
return _this._decrementCounts(definition).then(function () { return _this._updateSets(name, definition.sets); }).then(function () {
|
|
106
|
+
return _this.redis.del(_this.keys.buildDefinitionKey(name)).then(function (status) { return status === 1; });
|
|
107
|
+
});
|
|
106
108
|
});
|
|
107
109
|
};
|
|
108
110
|
/**
|