@splitsoftware/splitio-commons 2.5.0-rc.0 → 2.5.0
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 -3
- package/cjs/evaluator/convertions/index.js +9 -1
- package/cjs/evaluator/matchersTransform/index.js +2 -3
- package/cjs/sdkClient/sdkClientMethodCS.js +5 -1
- package/cjs/sdkFactory/index.js +8 -2
- package/cjs/storages/getRolloutPlan.js +69 -0
- package/cjs/storages/inLocalStorage/validateCache.js +2 -2
- package/cjs/storages/inMemory/InMemoryStorageCS.js +14 -32
- package/cjs/storages/setRolloutPlan.js +66 -0
- package/cjs/utils/inputValidation/index.js +1 -3
- package/cjs/utils/settingsValidation/index.js +4 -0
- package/esm/evaluator/convertions/index.js +7 -0
- package/esm/evaluator/matchersTransform/index.js +3 -4
- package/esm/sdkClient/sdkClientMethodCS.js +5 -1
- package/esm/sdkFactory/index.js +8 -2
- package/esm/storages/getRolloutPlan.js +65 -0
- package/esm/storages/inLocalStorage/validateCache.js +2 -2
- package/esm/storages/inMemory/InMemoryStorageCS.js +14 -32
- package/esm/storages/setRolloutPlan.js +61 -0
- package/esm/utils/inputValidation/index.js +0 -1
- package/esm/utils/settingsValidation/index.js +4 -0
- package/package.json +1 -1
- package/src/evaluator/convertions/index.ts +10 -0
- package/src/evaluator/matchersTransform/index.ts +3 -4
- package/src/sdkClient/sdkClientMethodCS.ts +7 -1
- package/src/sdkFactory/index.ts +9 -2
- package/src/storages/getRolloutPlan.ts +72 -0
- package/src/storages/inLocalStorage/validateCache.ts +2 -2
- package/src/storages/inMemory/InMemoryStorageCS.ts +14 -37
- package/src/storages/setRolloutPlan.ts +71 -0
- package/src/storages/types.ts +20 -2
- package/src/types.ts +2 -0
- package/src/utils/inputValidation/index.ts +0 -1
- package/src/utils/settingsValidation/index.ts +4 -0
- package/types/splitio.d.ts +30 -35
- package/cjs/storages/dataLoader.js +0 -109
- package/cjs/utils/inputValidation/preloadedData.js +0 -59
- package/esm/storages/dataLoader.js +0 -104
- package/esm/utils/inputValidation/preloadedData.js +0 -55
- package/src/storages/dataLoader.ts +0 -113
- package/src/utils/inputValidation/preloadedData.ts +0 -57
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCache = exports.setCache = void 0;
|
|
4
|
-
var sets_1 = require("../utils/lang/sets");
|
|
5
|
-
var key_1 = require("../utils/key");
|
|
6
|
-
/**
|
|
7
|
-
* Sets the given synchronous storage with the provided preloaded data snapshot.
|
|
8
|
-
* If `matchingKey` is provided, the storage is handled as a client-side storage (segments and largeSegments are instances of MySegmentsCache).
|
|
9
|
-
* Otherwise, the storage is handled as a server-side storage (segments is an instance of SegmentsCache).
|
|
10
|
-
*/
|
|
11
|
-
function setCache(log, preloadedData, storage, matchingKey) {
|
|
12
|
-
// Do not load data if current preloadedData is empty
|
|
13
|
-
if (Object.keys(preloadedData).length === 0)
|
|
14
|
-
return;
|
|
15
|
-
var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments, largeSegments = storage.largeSegments;
|
|
16
|
-
log.debug("set cache" + (matchingKey ? " for key " + matchingKey : ''));
|
|
17
|
-
if (splits) {
|
|
18
|
-
splits.clear();
|
|
19
|
-
splits.update(preloadedData.flags || [], [], preloadedData.since || -1);
|
|
20
|
-
}
|
|
21
|
-
if (rbSegments) {
|
|
22
|
-
rbSegments.clear();
|
|
23
|
-
rbSegments.update(preloadedData.rbSegments || [], [], preloadedData.rbSince || -1);
|
|
24
|
-
}
|
|
25
|
-
var segmentsData = preloadedData.segments || {};
|
|
26
|
-
if (matchingKey) { // add memberships data (client-side)
|
|
27
|
-
var memberships = preloadedData.memberships && preloadedData.memberships[matchingKey];
|
|
28
|
-
if (!memberships && segmentsData) {
|
|
29
|
-
memberships = {
|
|
30
|
-
ms: {
|
|
31
|
-
k: Object.keys(segmentsData).filter(function (segmentName) {
|
|
32
|
-
var segmentKeys = segmentsData[segmentName];
|
|
33
|
-
return segmentKeys.indexOf(matchingKey) > -1;
|
|
34
|
-
}).map(function (segmentName) { return ({ n: segmentName }); })
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
if (memberships) {
|
|
39
|
-
if (memberships.ms)
|
|
40
|
-
segments.resetSegments(memberships.ms);
|
|
41
|
-
if (memberships.ls && largeSegments)
|
|
42
|
-
largeSegments.resetSegments(memberships.ls);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
else { // add segments data (server-side)
|
|
46
|
-
Object.keys(segmentsData).forEach(function (segmentName) {
|
|
47
|
-
var segmentKeys = segmentsData[segmentName];
|
|
48
|
-
segments.update(segmentName, segmentKeys, [], -1);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
exports.setCache = setCache;
|
|
53
|
-
/**
|
|
54
|
-
* Gets the preloaded data snapshot from the given synchronous storage.
|
|
55
|
-
* If `keys` are provided, the memberships for those keys is returned, to protect segments data.
|
|
56
|
-
* Otherwise, the segments data is returned.
|
|
57
|
-
*/
|
|
58
|
-
function getCache(log, storage, keys) {
|
|
59
|
-
log.debug("get cache" + (keys ? " for keys " + keys : ''));
|
|
60
|
-
return {
|
|
61
|
-
since: storage.splits.getChangeNumber(),
|
|
62
|
-
flags: storage.splits.getAll(),
|
|
63
|
-
rbSince: storage.rbSegments.getChangeNumber(),
|
|
64
|
-
rbSegments: storage.rbSegments.getAll(),
|
|
65
|
-
segments: keys ?
|
|
66
|
-
undefined : // @ts-ignore accessing private prop
|
|
67
|
-
Object.keys(storage.segments.segmentCache).reduce(function (prev, cur) {
|
|
68
|
-
prev[cur] = (0, sets_1.setToArray)(storage.segments.segmentCache[cur]);
|
|
69
|
-
return prev;
|
|
70
|
-
}, {}),
|
|
71
|
-
memberships: keys ?
|
|
72
|
-
keys.reduce(function (prev, key) {
|
|
73
|
-
if (storage.shared) {
|
|
74
|
-
// Client-side segments
|
|
75
|
-
// @ts-ignore accessing private prop
|
|
76
|
-
var sharedStorage = storage.shared(key);
|
|
77
|
-
prev[(0, key_1.getMatching)(key)] = {
|
|
78
|
-
ms: {
|
|
79
|
-
// @ts-ignore accessing private prop
|
|
80
|
-
k: Object.keys(sharedStorage.segments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
|
|
81
|
-
},
|
|
82
|
-
ls: sharedStorage.largeSegments ? {
|
|
83
|
-
// @ts-ignore accessing private prop
|
|
84
|
-
k: Object.keys(sharedStorage.largeSegments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
|
|
85
|
-
} : undefined
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
prev[(0, key_1.getMatching)(key)] = {
|
|
90
|
-
ms: {
|
|
91
|
-
// Server-side segments
|
|
92
|
-
// @ts-ignore accessing private prop
|
|
93
|
-
k: Object.keys(storage.segments.segmentCache).reduce(function (prev, segmentName) {
|
|
94
|
-
return storage.segments.segmentCache[segmentName].has(key) ?
|
|
95
|
-
prev.concat({ n: segmentName }) :
|
|
96
|
-
prev;
|
|
97
|
-
}, [])
|
|
98
|
-
},
|
|
99
|
-
ls: {
|
|
100
|
-
k: []
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
return prev;
|
|
105
|
-
}, {}) :
|
|
106
|
-
undefined
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
exports.getCache = getCache;
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validatePreloadedData = void 0;
|
|
4
|
-
var lang_1 = require("../lang");
|
|
5
|
-
var split_1 = require("./split");
|
|
6
|
-
function validateTimestampData(log, maybeTimestamp, method, item) {
|
|
7
|
-
if ((0, lang_1.isFiniteNumber)(maybeTimestamp) && maybeTimestamp > -1)
|
|
8
|
-
return true;
|
|
9
|
-
log.error(method + ": preloadedData." + item + " must be a positive number.");
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
function validateSplitsData(log, maybeSplitsData, method) {
|
|
13
|
-
if ((0, lang_1.isObject)(maybeSplitsData)) {
|
|
14
|
-
var splitNames = Object.keys(maybeSplitsData);
|
|
15
|
-
if (splitNames.length === 0)
|
|
16
|
-
log.warn(method + ": preloadedData.splitsData doesn't contain feature flag definitions.");
|
|
17
|
-
// @TODO in the future, consider handling the possibility of having parsed definitions of splits
|
|
18
|
-
if (splitNames.every(function (splitName) { return (0, split_1.validateSplit)(log, splitName, method) && (0, lang_1.isString)(maybeSplitsData[splitName]); }))
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
log.error(method + ": preloadedData.splitsData must be a map of feature flag names to their stringified definitions.");
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
function validateMySegmentsData(log, maybeMySegmentsData, method) {
|
|
25
|
-
if ((0, lang_1.isObject)(maybeMySegmentsData)) {
|
|
26
|
-
var userKeys = Object.keys(maybeMySegmentsData);
|
|
27
|
-
if (userKeys.every(function (userKey) {
|
|
28
|
-
var segmentNames = maybeMySegmentsData[userKey];
|
|
29
|
-
// an empty list is valid
|
|
30
|
-
return Array.isArray(segmentNames) && segmentNames.every(function (segmentName) { return (0, lang_1.isString)(segmentName); });
|
|
31
|
-
}))
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
log.error(method + ": preloadedData.mySegmentsData must be a map of user keys to their list of segment names.");
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
function validateSegmentsData(log, maybeSegmentsData, method) {
|
|
38
|
-
if ((0, lang_1.isObject)(maybeSegmentsData)) {
|
|
39
|
-
var segmentNames = Object.keys(maybeSegmentsData);
|
|
40
|
-
if (segmentNames.every(function (segmentName) { return (0, lang_1.isString)(maybeSegmentsData[segmentName]); }))
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
log.error(method + ": preloadedData.segmentsData must be a map of segment names to their stringified definitions.");
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
function validatePreloadedData(log, maybePreloadedData, method) {
|
|
47
|
-
if (!(0, lang_1.isObject)(maybePreloadedData)) {
|
|
48
|
-
log.error(method + ": preloadedData must be an object.");
|
|
49
|
-
}
|
|
50
|
-
else if (validateTimestampData(log, maybePreloadedData.lastUpdated, method, 'lastUpdated') &&
|
|
51
|
-
validateTimestampData(log, maybePreloadedData.since, method, 'since') &&
|
|
52
|
-
validateSplitsData(log, maybePreloadedData.splitsData, method) &&
|
|
53
|
-
(!maybePreloadedData.mySegmentsData || validateMySegmentsData(log, maybePreloadedData.mySegmentsData, method)) &&
|
|
54
|
-
(!maybePreloadedData.segmentsData || validateSegmentsData(log, maybePreloadedData.segmentsData, method))) {
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
exports.validatePreloadedData = validatePreloadedData;
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { setToArray } from '../utils/lang/sets';
|
|
2
|
-
import { getMatching } from '../utils/key';
|
|
3
|
-
/**
|
|
4
|
-
* Sets the given synchronous storage with the provided preloaded data snapshot.
|
|
5
|
-
* If `matchingKey` is provided, the storage is handled as a client-side storage (segments and largeSegments are instances of MySegmentsCache).
|
|
6
|
-
* Otherwise, the storage is handled as a server-side storage (segments is an instance of SegmentsCache).
|
|
7
|
-
*/
|
|
8
|
-
export function setCache(log, preloadedData, storage, matchingKey) {
|
|
9
|
-
// Do not load data if current preloadedData is empty
|
|
10
|
-
if (Object.keys(preloadedData).length === 0)
|
|
11
|
-
return;
|
|
12
|
-
var splits = storage.splits, rbSegments = storage.rbSegments, segments = storage.segments, largeSegments = storage.largeSegments;
|
|
13
|
-
log.debug("set cache" + (matchingKey ? " for key " + matchingKey : ''));
|
|
14
|
-
if (splits) {
|
|
15
|
-
splits.clear();
|
|
16
|
-
splits.update(preloadedData.flags || [], [], preloadedData.since || -1);
|
|
17
|
-
}
|
|
18
|
-
if (rbSegments) {
|
|
19
|
-
rbSegments.clear();
|
|
20
|
-
rbSegments.update(preloadedData.rbSegments || [], [], preloadedData.rbSince || -1);
|
|
21
|
-
}
|
|
22
|
-
var segmentsData = preloadedData.segments || {};
|
|
23
|
-
if (matchingKey) { // add memberships data (client-side)
|
|
24
|
-
var memberships = preloadedData.memberships && preloadedData.memberships[matchingKey];
|
|
25
|
-
if (!memberships && segmentsData) {
|
|
26
|
-
memberships = {
|
|
27
|
-
ms: {
|
|
28
|
-
k: Object.keys(segmentsData).filter(function (segmentName) {
|
|
29
|
-
var segmentKeys = segmentsData[segmentName];
|
|
30
|
-
return segmentKeys.indexOf(matchingKey) > -1;
|
|
31
|
-
}).map(function (segmentName) { return ({ n: segmentName }); })
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
if (memberships) {
|
|
36
|
-
if (memberships.ms)
|
|
37
|
-
segments.resetSegments(memberships.ms);
|
|
38
|
-
if (memberships.ls && largeSegments)
|
|
39
|
-
largeSegments.resetSegments(memberships.ls);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
else { // add segments data (server-side)
|
|
43
|
-
Object.keys(segmentsData).forEach(function (segmentName) {
|
|
44
|
-
var segmentKeys = segmentsData[segmentName];
|
|
45
|
-
segments.update(segmentName, segmentKeys, [], -1);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Gets the preloaded data snapshot from the given synchronous storage.
|
|
51
|
-
* If `keys` are provided, the memberships for those keys is returned, to protect segments data.
|
|
52
|
-
* Otherwise, the segments data is returned.
|
|
53
|
-
*/
|
|
54
|
-
export function getCache(log, storage, keys) {
|
|
55
|
-
log.debug("get cache" + (keys ? " for keys " + keys : ''));
|
|
56
|
-
return {
|
|
57
|
-
since: storage.splits.getChangeNumber(),
|
|
58
|
-
flags: storage.splits.getAll(),
|
|
59
|
-
rbSince: storage.rbSegments.getChangeNumber(),
|
|
60
|
-
rbSegments: storage.rbSegments.getAll(),
|
|
61
|
-
segments: keys ?
|
|
62
|
-
undefined : // @ts-ignore accessing private prop
|
|
63
|
-
Object.keys(storage.segments.segmentCache).reduce(function (prev, cur) {
|
|
64
|
-
prev[cur] = setToArray(storage.segments.segmentCache[cur]);
|
|
65
|
-
return prev;
|
|
66
|
-
}, {}),
|
|
67
|
-
memberships: keys ?
|
|
68
|
-
keys.reduce(function (prev, key) {
|
|
69
|
-
if (storage.shared) {
|
|
70
|
-
// Client-side segments
|
|
71
|
-
// @ts-ignore accessing private prop
|
|
72
|
-
var sharedStorage = storage.shared(key);
|
|
73
|
-
prev[getMatching(key)] = {
|
|
74
|
-
ms: {
|
|
75
|
-
// @ts-ignore accessing private prop
|
|
76
|
-
k: Object.keys(sharedStorage.segments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
|
|
77
|
-
},
|
|
78
|
-
ls: sharedStorage.largeSegments ? {
|
|
79
|
-
// @ts-ignore accessing private prop
|
|
80
|
-
k: Object.keys(sharedStorage.largeSegments.segmentCache).map(function (segmentName) { return ({ n: segmentName }); }),
|
|
81
|
-
} : undefined
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
prev[getMatching(key)] = {
|
|
86
|
-
ms: {
|
|
87
|
-
// Server-side segments
|
|
88
|
-
// @ts-ignore accessing private prop
|
|
89
|
-
k: Object.keys(storage.segments.segmentCache).reduce(function (prev, segmentName) {
|
|
90
|
-
return storage.segments.segmentCache[segmentName].has(key) ?
|
|
91
|
-
prev.concat({ n: segmentName }) :
|
|
92
|
-
prev;
|
|
93
|
-
}, [])
|
|
94
|
-
},
|
|
95
|
-
ls: {
|
|
96
|
-
k: []
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return prev;
|
|
101
|
-
}, {}) :
|
|
102
|
-
undefined
|
|
103
|
-
};
|
|
104
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { isObject, isString, isFiniteNumber } from '../lang';
|
|
2
|
-
import { validateSplit } from './split';
|
|
3
|
-
function validateTimestampData(log, maybeTimestamp, method, item) {
|
|
4
|
-
if (isFiniteNumber(maybeTimestamp) && maybeTimestamp > -1)
|
|
5
|
-
return true;
|
|
6
|
-
log.error(method + ": preloadedData." + item + " must be a positive number.");
|
|
7
|
-
return false;
|
|
8
|
-
}
|
|
9
|
-
function validateSplitsData(log, maybeSplitsData, method) {
|
|
10
|
-
if (isObject(maybeSplitsData)) {
|
|
11
|
-
var splitNames = Object.keys(maybeSplitsData);
|
|
12
|
-
if (splitNames.length === 0)
|
|
13
|
-
log.warn(method + ": preloadedData.splitsData doesn't contain feature flag definitions.");
|
|
14
|
-
// @TODO in the future, consider handling the possibility of having parsed definitions of splits
|
|
15
|
-
if (splitNames.every(function (splitName) { return validateSplit(log, splitName, method) && isString(maybeSplitsData[splitName]); }))
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
log.error(method + ": preloadedData.splitsData must be a map of feature flag names to their stringified definitions.");
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
function validateMySegmentsData(log, maybeMySegmentsData, method) {
|
|
22
|
-
if (isObject(maybeMySegmentsData)) {
|
|
23
|
-
var userKeys = Object.keys(maybeMySegmentsData);
|
|
24
|
-
if (userKeys.every(function (userKey) {
|
|
25
|
-
var segmentNames = maybeMySegmentsData[userKey];
|
|
26
|
-
// an empty list is valid
|
|
27
|
-
return Array.isArray(segmentNames) && segmentNames.every(function (segmentName) { return isString(segmentName); });
|
|
28
|
-
}))
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
log.error(method + ": preloadedData.mySegmentsData must be a map of user keys to their list of segment names.");
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
function validateSegmentsData(log, maybeSegmentsData, method) {
|
|
35
|
-
if (isObject(maybeSegmentsData)) {
|
|
36
|
-
var segmentNames = Object.keys(maybeSegmentsData);
|
|
37
|
-
if (segmentNames.every(function (segmentName) { return isString(maybeSegmentsData[segmentName]); }))
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
log.error(method + ": preloadedData.segmentsData must be a map of segment names to their stringified definitions.");
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
export function validatePreloadedData(log, maybePreloadedData, method) {
|
|
44
|
-
if (!isObject(maybePreloadedData)) {
|
|
45
|
-
log.error(method + ": preloadedData must be an object.");
|
|
46
|
-
}
|
|
47
|
-
else if (validateTimestampData(log, maybePreloadedData.lastUpdated, method, 'lastUpdated') &&
|
|
48
|
-
validateTimestampData(log, maybePreloadedData.since, method, 'since') &&
|
|
49
|
-
validateSplitsData(log, maybePreloadedData.splitsData, method) &&
|
|
50
|
-
(!maybePreloadedData.mySegmentsData || validateMySegmentsData(log, maybePreloadedData.mySegmentsData, method)) &&
|
|
51
|
-
(!maybePreloadedData.segmentsData || validateSegmentsData(log, maybePreloadedData.segmentsData, method))) {
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import SplitIO from '../../types/splitio';
|
|
2
|
-
import { IRBSegmentsCacheSync, ISegmentsCacheSync, ISplitsCacheSync, IStorageSync } from './types';
|
|
3
|
-
import { setToArray } from '../utils/lang/sets';
|
|
4
|
-
import { getMatching } from '../utils/key';
|
|
5
|
-
import { IMembershipsResponse, IMySegmentsResponse, IRBSegment, ISplit } from '../dtos/types';
|
|
6
|
-
import { ILogger } from '../logger/types';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Sets the given synchronous storage with the provided preloaded data snapshot.
|
|
10
|
-
* If `matchingKey` is provided, the storage is handled as a client-side storage (segments and largeSegments are instances of MySegmentsCache).
|
|
11
|
-
* Otherwise, the storage is handled as a server-side storage (segments is an instance of SegmentsCache).
|
|
12
|
-
*/
|
|
13
|
-
export function setCache(log: ILogger, preloadedData: SplitIO.PreloadedData, storage: { splits?: ISplitsCacheSync, rbSegments?: IRBSegmentsCacheSync, segments: ISegmentsCacheSync, largeSegments?: ISegmentsCacheSync }, matchingKey?: string) {
|
|
14
|
-
// Do not load data if current preloadedData is empty
|
|
15
|
-
if (Object.keys(preloadedData).length === 0) return;
|
|
16
|
-
|
|
17
|
-
const { splits, rbSegments, segments, largeSegments } = storage;
|
|
18
|
-
|
|
19
|
-
log.debug(`set cache${matchingKey ? ` for key ${matchingKey}` : ''}`);
|
|
20
|
-
|
|
21
|
-
if (splits) {
|
|
22
|
-
splits.clear();
|
|
23
|
-
splits.update(preloadedData.flags as ISplit[] || [], [], preloadedData.since || -1);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (rbSegments) {
|
|
27
|
-
rbSegments.clear();
|
|
28
|
-
rbSegments.update(preloadedData.rbSegments as IRBSegment[] || [], [], preloadedData.rbSince || -1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const segmentsData = preloadedData.segments || {};
|
|
32
|
-
if (matchingKey) { // add memberships data (client-side)
|
|
33
|
-
let memberships = preloadedData.memberships && preloadedData.memberships[matchingKey];
|
|
34
|
-
if (!memberships && segmentsData) {
|
|
35
|
-
memberships = {
|
|
36
|
-
ms: {
|
|
37
|
-
k: Object.keys(segmentsData).filter(segmentName => {
|
|
38
|
-
const segmentKeys = segmentsData[segmentName];
|
|
39
|
-
return segmentKeys.indexOf(matchingKey) > -1;
|
|
40
|
-
}).map(segmentName => ({ n: segmentName }))
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (memberships) {
|
|
46
|
-
if ((memberships as IMembershipsResponse).ms) segments.resetSegments((memberships as IMembershipsResponse).ms!);
|
|
47
|
-
if ((memberships as IMembershipsResponse).ls && largeSegments) largeSegments.resetSegments((memberships as IMembershipsResponse).ls!);
|
|
48
|
-
}
|
|
49
|
-
} else { // add segments data (server-side)
|
|
50
|
-
Object.keys(segmentsData).forEach(segmentName => {
|
|
51
|
-
const segmentKeys = segmentsData[segmentName];
|
|
52
|
-
segments.update(segmentName, segmentKeys, [], -1);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Gets the preloaded data snapshot from the given synchronous storage.
|
|
59
|
-
* If `keys` are provided, the memberships for those keys is returned, to protect segments data.
|
|
60
|
-
* Otherwise, the segments data is returned.
|
|
61
|
-
*/
|
|
62
|
-
export function getCache(log: ILogger, storage: IStorageSync, keys?: SplitIO.SplitKey[]): SplitIO.PreloadedData {
|
|
63
|
-
|
|
64
|
-
log.debug(`get cache${keys ? ` for keys ${keys}` : ''}`);
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
since: storage.splits.getChangeNumber(),
|
|
68
|
-
flags: storage.splits.getAll(),
|
|
69
|
-
rbSince: storage.rbSegments.getChangeNumber(),
|
|
70
|
-
rbSegments: storage.rbSegments.getAll(),
|
|
71
|
-
segments: keys ?
|
|
72
|
-
undefined : // @ts-ignore accessing private prop
|
|
73
|
-
Object.keys(storage.segments.segmentCache).reduce((prev, cur) => { // @ts-ignore accessing private prop
|
|
74
|
-
prev[cur] = setToArray(storage.segments.segmentCache[cur] as Set<string>);
|
|
75
|
-
return prev;
|
|
76
|
-
}, {}),
|
|
77
|
-
memberships: keys ?
|
|
78
|
-
keys.reduce<Record<string, IMembershipsResponse>>((prev, key) => {
|
|
79
|
-
if (storage.shared) {
|
|
80
|
-
// Client-side segments
|
|
81
|
-
// @ts-ignore accessing private prop
|
|
82
|
-
const sharedStorage = storage.shared(key);
|
|
83
|
-
prev[getMatching(key)] = {
|
|
84
|
-
ms: {
|
|
85
|
-
// @ts-ignore accessing private prop
|
|
86
|
-
k: Object.keys(sharedStorage.segments.segmentCache).map(segmentName => ({ n: segmentName })),
|
|
87
|
-
},
|
|
88
|
-
ls: sharedStorage.largeSegments ? {
|
|
89
|
-
// @ts-ignore accessing private prop
|
|
90
|
-
k: Object.keys(sharedStorage.largeSegments.segmentCache).map(segmentName => ({ n: segmentName })),
|
|
91
|
-
} : undefined
|
|
92
|
-
};
|
|
93
|
-
} else {
|
|
94
|
-
prev[getMatching(key)] = {
|
|
95
|
-
ms: {
|
|
96
|
-
// Server-side segments
|
|
97
|
-
// @ts-ignore accessing private prop
|
|
98
|
-
k: Object.keys(storage.segments.segmentCache).reduce<IMySegmentsResponse['k']>((prev, segmentName) => { // @ts-ignore accessing private prop
|
|
99
|
-
return storage.segments.segmentCache[segmentName].has(key) ?
|
|
100
|
-
prev!.concat({ n: segmentName }) :
|
|
101
|
-
prev;
|
|
102
|
-
}, [])
|
|
103
|
-
},
|
|
104
|
-
ls: {
|
|
105
|
-
k: []
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
return prev;
|
|
110
|
-
}, {}) :
|
|
111
|
-
undefined
|
|
112
|
-
};
|
|
113
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { isObject, isString, isFiniteNumber } from '../lang';
|
|
2
|
-
import { validateSplit } from './split';
|
|
3
|
-
import { ILogger } from '../../logger/types';
|
|
4
|
-
|
|
5
|
-
function validateTimestampData(log: ILogger, maybeTimestamp: any, method: string, item: string) {
|
|
6
|
-
if (isFiniteNumber(maybeTimestamp) && maybeTimestamp > -1) return true;
|
|
7
|
-
log.error(`${method}: preloadedData.${item} must be a positive number.`);
|
|
8
|
-
return false;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function validateSplitsData(log: ILogger, maybeSplitsData: any, method: string) {
|
|
12
|
-
if (isObject(maybeSplitsData)) {
|
|
13
|
-
const splitNames = Object.keys(maybeSplitsData);
|
|
14
|
-
if (splitNames.length === 0) log.warn(`${method}: preloadedData.splitsData doesn't contain feature flag definitions.`);
|
|
15
|
-
// @TODO in the future, consider handling the possibility of having parsed definitions of splits
|
|
16
|
-
if (splitNames.every(splitName => validateSplit(log, splitName, method) && isString(maybeSplitsData[splitName]))) return true;
|
|
17
|
-
}
|
|
18
|
-
log.error(`${method}: preloadedData.splitsData must be a map of feature flag names to their stringified definitions.`);
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function validateMySegmentsData(log: ILogger, maybeMySegmentsData: any, method: string) {
|
|
23
|
-
if (isObject(maybeMySegmentsData)) {
|
|
24
|
-
const userKeys = Object.keys(maybeMySegmentsData);
|
|
25
|
-
if (userKeys.every(userKey => {
|
|
26
|
-
const segmentNames = maybeMySegmentsData[userKey];
|
|
27
|
-
// an empty list is valid
|
|
28
|
-
return Array.isArray(segmentNames) && segmentNames.every(segmentName => isString(segmentName));
|
|
29
|
-
})) return true;
|
|
30
|
-
}
|
|
31
|
-
log.error(`${method}: preloadedData.mySegmentsData must be a map of user keys to their list of segment names.`);
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function validateSegmentsData(log: ILogger, maybeSegmentsData: any, method: string) {
|
|
36
|
-
if (isObject(maybeSegmentsData)) {
|
|
37
|
-
const segmentNames = Object.keys(maybeSegmentsData);
|
|
38
|
-
if (segmentNames.every(segmentName => isString(maybeSegmentsData[segmentName]))) return true;
|
|
39
|
-
}
|
|
40
|
-
log.error(`${method}: preloadedData.segmentsData must be a map of segment names to their stringified definitions.`);
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function validatePreloadedData(log: ILogger, maybePreloadedData: any, method: string) {
|
|
45
|
-
if (!isObject(maybePreloadedData)) {
|
|
46
|
-
log.error(`${method}: preloadedData must be an object.`);
|
|
47
|
-
} else if (
|
|
48
|
-
validateTimestampData(log, maybePreloadedData.lastUpdated, method, 'lastUpdated') &&
|
|
49
|
-
validateTimestampData(log, maybePreloadedData.since, method, 'since') &&
|
|
50
|
-
validateSplitsData(log, maybePreloadedData.splitsData, method) &&
|
|
51
|
-
(!maybePreloadedData.mySegmentsData || validateMySegmentsData(log, maybePreloadedData.mySegmentsData, method)) &&
|
|
52
|
-
(!maybePreloadedData.segmentsData || validateSegmentsData(log, maybePreloadedData.segmentsData, method))
|
|
53
|
-
) {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
return false;
|
|
57
|
-
}
|