@splitsoftware/splitio-commons 1.6.2-rc.7 → 1.6.2-rc.9
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 -1
- package/cjs/consent/sdkUserConsent.js +2 -2
- package/cjs/evaluator/index.js +5 -5
- package/cjs/listeners/browser.js +1 -2
- package/cjs/logger/constants.js +1 -2
- package/cjs/sdkClient/client.js +19 -7
- package/cjs/sdkClient/sdkClient.js +1 -3
- package/cjs/sdkFactory/index.js +5 -26
- package/cjs/services/splitApi.js +4 -24
- package/cjs/storages/KeyBuilderSS.js +48 -15
- package/cjs/storages/inLocalStorage/index.js +1 -5
- package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -12
- package/cjs/storages/inMemory/InMemoryStorage.js +2 -6
- package/cjs/storages/inMemory/InMemoryStorageCS.js +2 -6
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +9 -6
- package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +100 -0
- package/cjs/storages/inRedis/constants.js +1 -4
- package/cjs/storages/inRedis/index.js +1 -15
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +126 -0
- package/cjs/storages/pluggable/index.js +19 -15
- package/cjs/sync/submitters/submitterManager.js +0 -3
- package/cjs/sync/submitters/telemetrySubmitter.js +7 -5
- package/cjs/trackers/impressionsTracker.js +41 -22
- package/cjs/utils/constants/index.js +2 -4
- package/cjs/utils/lang/maps.js +15 -7
- package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
- package/cjs/utils/settingsValidation/index.js +0 -4
- package/esm/consent/sdkUserConsent.js +2 -2
- package/esm/evaluator/index.js +5 -5
- package/esm/listeners/browser.js +2 -3
- package/esm/logger/constants.js +0 -1
- package/esm/sdkClient/client.js +19 -7
- package/esm/sdkClient/sdkClient.js +1 -3
- package/esm/sdkFactory/index.js +5 -26
- package/esm/services/splitApi.js +4 -24
- package/esm/storages/KeyBuilderSS.js +44 -14
- package/esm/storages/inLocalStorage/index.js +2 -6
- package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -12
- package/esm/storages/inMemory/InMemoryStorage.js +4 -8
- package/esm/storages/inMemory/InMemoryStorageCS.js +3 -7
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +8 -6
- package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +100 -0
- package/esm/storages/inRedis/constants.js +0 -3
- package/esm/storages/inRedis/index.js +2 -16
- package/esm/storages/pluggable/TelemetryCachePluggable.js +126 -0
- package/esm/storages/pluggable/index.js +19 -15
- package/esm/sync/submitters/submitterManager.js +0 -3
- package/esm/sync/submitters/telemetrySubmitter.js +8 -6
- package/esm/trackers/impressionsTracker.js +41 -22
- package/esm/utils/constants/index.js +0 -2
- package/esm/utils/lang/maps.js +15 -7
- package/esm/utils/settingsValidation/impressionsMode.js +3 -3
- package/esm/utils/settingsValidation/index.js +0 -4
- package/package.json +2 -1
- package/src/consent/sdkUserConsent.ts +2 -2
- package/src/evaluator/index.ts +6 -6
- package/src/listeners/browser.ts +2 -3
- package/src/logger/.DS_Store +0 -0
- package/src/logger/constants.ts +0 -1
- package/src/sdkClient/client.ts +21 -8
- package/src/sdkClient/sdkClient.ts +1 -3
- package/src/sdkFactory/index.ts +5 -29
- package/src/sdkFactory/types.ts +4 -7
- package/src/services/splitApi.ts +4 -26
- package/src/services/types.ts +2 -8
- package/src/storages/KeyBuilderSS.ts +53 -17
- package/src/storages/inLocalStorage/index.ts +2 -5
- package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +1 -16
- package/src/storages/inMemory/InMemoryStorage.ts +4 -7
- package/src/storages/inMemory/InMemoryStorageCS.ts +3 -7
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +9 -7
- package/src/storages/inRedis/EventsCacheInRedis.ts +1 -1
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +122 -2
- package/src/storages/inRedis/constants.ts +0 -3
- package/src/storages/inRedis/index.ts +3 -12
- package/src/storages/pluggable/TelemetryCachePluggable.ts +147 -2
- package/src/storages/pluggable/index.ts +20 -16
- package/src/storages/types.ts +13 -34
- package/src/sync/submitters/submitterManager.ts +0 -2
- package/src/sync/submitters/telemetrySubmitter.ts +14 -9
- package/src/sync/submitters/types.ts +40 -26
- package/src/trackers/impressionsTracker.ts +48 -27
- package/src/trackers/types.ts +0 -28
- package/src/types.ts +1 -5
- package/src/utils/constants/index.ts +0 -2
- package/src/utils/lang/maps.ts +20 -8
- package/src/utils/settingsValidation/impressionsMode.ts +3 -3
- package/src/utils/settingsValidation/index.ts +0 -5
- package/types/logger/constants.d.ts +0 -1
- package/types/sdkFactory/types.d.ts +2 -4
- package/types/services/types.d.ts +2 -6
- package/types/storages/KeyBuilderSS.d.ts +7 -4
- package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +1 -5
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +6 -3
- package/types/storages/inRedis/EventsCacheInRedis.d.ts +1 -1
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +16 -1
- package/types/storages/inRedis/constants.d.ts +0 -3
- package/types/storages/pluggable/TelemetryCachePluggable.d.ts +17 -1
- package/types/storages/types.d.ts +10 -21
- package/types/sync/submitters/telemetrySubmitter.d.ts +1 -1
- package/types/sync/submitters/types.d.ts +13 -24
- package/types/trackers/impressionsTracker.d.ts +6 -4
- package/types/trackers/types.d.ts +0 -23
- package/types/types.d.ts +1 -5
- package/types/utils/constants/index.d.ts +0 -2
- package/types/utils/lang/maps.d.ts +6 -2
- package/types/utils/settingsValidation/index.d.ts +0 -1
- package/cjs/storages/inMemory/uniqueKeysCacheInMemory.js +0 -73
- package/cjs/storages/inMemory/uniqueKeysCacheInMemoryCS.js +0 -78
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +0 -49
- package/cjs/storages/inRedis/uniqueKeysCacheInRedis.js +0 -56
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +0 -26
- package/cjs/trackers/strategy/strategyDebug.js +0 -25
- package/cjs/trackers/strategy/strategyNone.js +0 -29
- package/cjs/trackers/strategy/strategyOptimized.js +0 -35
- package/cjs/trackers/uniqueKeysTracker.js +0 -38
- package/esm/storages/inMemory/uniqueKeysCacheInMemory.js +0 -70
- package/esm/storages/inMemory/uniqueKeysCacheInMemoryCS.js +0 -75
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +0 -46
- package/esm/storages/inRedis/uniqueKeysCacheInRedis.js +0 -53
- package/esm/sync/submitters/uniqueKeysSubmitter.js +0 -22
- package/esm/trackers/strategy/strategyDebug.js +0 -21
- package/esm/trackers/strategy/strategyNone.js +0 -25
- package/esm/trackers/strategy/strategyOptimized.js +0 -31
- package/esm/trackers/uniqueKeysTracker.js +0 -34
- package/src/storages/inMemory/uniqueKeysCacheInMemory.ts +0 -82
- package/src/storages/inMemory/uniqueKeysCacheInMemoryCS.ts +0 -88
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +0 -51
- package/src/storages/inRedis/uniqueKeysCacheInRedis.ts +0 -63
- package/src/sync/submitters/uniqueKeysSubmitter.ts +0 -35
- package/src/trackers/strategy/strategyDebug.ts +0 -28
- package/src/trackers/strategy/strategyNone.ts +0 -34
- package/src/trackers/strategy/strategyOptimized.ts +0 -42
- package/src/trackers/uniqueKeysTracker.ts +0 -48
- package/types/sdkClient/types.d.ts +0 -18
- package/types/storages/inMemory/CountsCacheInMemory.d.ts +0 -20
- package/types/storages/inMemory/LatenciesCacheInMemory.d.ts +0 -20
- package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +0 -35
- package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +0 -37
- package/types/storages/inRedis/CountsCacheInRedis.d.ts +0 -9
- package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +0 -14
- package/types/storages/inRedis/LatenciesCacheInRedis.d.ts +0 -9
- package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +0 -15
- package/types/sync/offline/LocalhostFromFile.d.ts +0 -2
- package/types/sync/offline/splitsParser/splitsParserFromFile.d.ts +0 -2
- package/types/sync/submitters/eventsSyncTask.d.ts +0 -8
- package/types/sync/submitters/impressionCountsSubmitterInRedis.d.ts +0 -5
- package/types/sync/submitters/impressionCountsSyncTask.d.ts +0 -13
- package/types/sync/submitters/impressionsSyncTask.d.ts +0 -14
- package/types/sync/submitters/metricsSyncTask.d.ts +0 -12
- package/types/sync/submitters/submitterSyncTask.d.ts +0 -10
- package/types/sync/submitters/uniqueKeysSubmitter.d.ts +0 -5
- package/types/sync/submitters/uniqueKeysSubmitterInRedis.d.ts +0 -5
- package/types/sync/syncTaskComposite.d.ts +0 -5
- package/types/trackers/filter/bloomFilter.d.ts +0 -10
- package/types/trackers/filter/dictionaryFilter.d.ts +0 -8
- package/types/trackers/filter/types.d.ts +0 -5
- package/types/trackers/strategy/strategyDebug.d.ts +0 -9
- package/types/trackers/strategy/strategyNone.d.ts +0 -10
- package/types/trackers/strategy/strategyOptimized.d.ts +0 -11
- package/types/trackers/uniqueKeysTracker.d.ts +0 -13
- package/types/utils/timeTracker/index.d.ts +0 -70
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
import { parseExceptionField, parseLatencyField, parseMetadata } from '../KeyBuilderSS';
|
|
1
2
|
import { findLatencyIndex } from '../findLatencyIndex';
|
|
3
|
+
import { getTelemetryConfigStats } from '../../sync/submitters/telemetrySubmitter';
|
|
4
|
+
import { CONSUMER_MODE, STORAGE_PLUGGABLE } from '../../utils/constants';
|
|
5
|
+
import { isString, isNaNNumber } from '../../utils/lang';
|
|
6
|
+
import { _Map } from '../../utils/lang/maps';
|
|
7
|
+
import { MAX_LATENCY_BUCKET_COUNT, newBuckets } from '../inMemory/TelemetryCacheInMemory';
|
|
2
8
|
var TelemetryCachePluggable = /** @class */ (function () {
|
|
3
9
|
/**
|
|
4
10
|
* Create a Telemetry cache that uses a storage wrapper.
|
|
@@ -19,6 +25,126 @@ var TelemetryCachePluggable = /** @class */ (function () {
|
|
|
19
25
|
return this.wrapper.incr(this.keys.buildExceptionKey(method))
|
|
20
26
|
.catch(function () { });
|
|
21
27
|
};
|
|
28
|
+
TelemetryCachePluggable.prototype.recordConfig = function () {
|
|
29
|
+
var value = JSON.stringify(getTelemetryConfigStats(CONSUMER_MODE, STORAGE_PLUGGABLE));
|
|
30
|
+
return this.wrapper.set(this.keys.buildInitKey(), value).catch(function () { });
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Pop telemetry latencies.
|
|
34
|
+
* The returned promise rejects if wrapper operations fail.
|
|
35
|
+
*/
|
|
36
|
+
TelemetryCachePluggable.prototype.popLatencies = function () {
|
|
37
|
+
var _this = this;
|
|
38
|
+
return this.wrapper.getKeysByPrefix(this.keys.latencyPrefix).then(function (latencyKeys) {
|
|
39
|
+
return latencyKeys.length ?
|
|
40
|
+
_this.wrapper.getMany(latencyKeys).then(function (latencies) {
|
|
41
|
+
var result = new _Map();
|
|
42
|
+
for (var i = 0; i < latencyKeys.length; i++) {
|
|
43
|
+
var field = latencyKeys[i].split('::')[1];
|
|
44
|
+
var parsedField = parseLatencyField(field);
|
|
45
|
+
if (isString(parsedField)) {
|
|
46
|
+
_this.log.error("Ignoring invalid latency field: " + field + ": " + parsedField);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
var count = parseInt(latencies[i]);
|
|
51
|
+
if (isNaNNumber(count)) {
|
|
52
|
+
_this.log.error("Ignoring latency with invalid count: " + latencies[i]);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
var metadata = parsedField[0], method = parsedField[1], bucket = parsedField[2];
|
|
56
|
+
if (bucket >= MAX_LATENCY_BUCKET_COUNT) {
|
|
57
|
+
_this.log.error("Ignoring latency with invalid bucket: " + bucket);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!result.has(metadata))
|
|
61
|
+
result.set(metadata, {
|
|
62
|
+
t: newBuckets(),
|
|
63
|
+
ts: newBuckets(),
|
|
64
|
+
tc: newBuckets(),
|
|
65
|
+
tcs: newBuckets(),
|
|
66
|
+
tr: newBuckets(),
|
|
67
|
+
});
|
|
68
|
+
result.get(metadata)[method][bucket] = count;
|
|
69
|
+
}
|
|
70
|
+
return Promise.all(latencyKeys.map(function (latencyKey) { return _this.wrapper.del(latencyKey); })).then(function () { return result; });
|
|
71
|
+
}) :
|
|
72
|
+
// If latencyKeys is empty, return an empty map.
|
|
73
|
+
new _Map();
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Pop telemetry exceptions.
|
|
78
|
+
* The returned promise rejects if wrapper operations fail.
|
|
79
|
+
*/
|
|
80
|
+
TelemetryCachePluggable.prototype.popExceptions = function () {
|
|
81
|
+
var _this = this;
|
|
82
|
+
return this.wrapper.getKeysByPrefix(this.keys.exceptionPrefix).then(function (exceptionKeys) {
|
|
83
|
+
return exceptionKeys.length ?
|
|
84
|
+
_this.wrapper.getMany(exceptionKeys).then(function (exceptions) {
|
|
85
|
+
var result = new _Map();
|
|
86
|
+
for (var i = 0; i < exceptionKeys.length; i++) {
|
|
87
|
+
var field = exceptionKeys[i].split('::')[1];
|
|
88
|
+
var parsedField = parseExceptionField(field);
|
|
89
|
+
if (isString(parsedField)) {
|
|
90
|
+
_this.log.error("Ignoring invalid exception field: " + field + ": " + parsedField);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
var count = parseInt(exceptions[i]);
|
|
95
|
+
if (isNaNNumber(count)) {
|
|
96
|
+
_this.log.error("Ignoring exception with invalid count: " + exceptions[i]);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
var metadata = parsedField[0], method = parsedField[1];
|
|
100
|
+
if (!result.has(metadata))
|
|
101
|
+
result.set(metadata, {
|
|
102
|
+
t: 0,
|
|
103
|
+
ts: 0,
|
|
104
|
+
tc: 0,
|
|
105
|
+
tcs: 0,
|
|
106
|
+
tr: 0,
|
|
107
|
+
});
|
|
108
|
+
result.get(metadata)[method] = count;
|
|
109
|
+
}
|
|
110
|
+
return Promise.all(exceptionKeys.map(function (exceptionKey) { return _this.wrapper.del(exceptionKey); })).then(function () { return result; });
|
|
111
|
+
}) :
|
|
112
|
+
// If exceptionKeys is empty, return an empty map.
|
|
113
|
+
new _Map();
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Pop telemetry configs.
|
|
118
|
+
* The returned promise rejects if wrapper operations fail.
|
|
119
|
+
*/
|
|
120
|
+
TelemetryCachePluggable.prototype.popConfigs = function () {
|
|
121
|
+
var _this = this;
|
|
122
|
+
return this.wrapper.getKeysByPrefix(this.keys.initPrefix).then(function (configKeys) {
|
|
123
|
+
return configKeys.length ?
|
|
124
|
+
_this.wrapper.getMany(configKeys).then(function (configs) {
|
|
125
|
+
var result = new _Map();
|
|
126
|
+
for (var i = 0; i < configKeys.length; i++) {
|
|
127
|
+
var field = configKeys[i].split('::')[1];
|
|
128
|
+
var parsedField = parseMetadata(field);
|
|
129
|
+
if (isString(parsedField)) {
|
|
130
|
+
_this.log.error("Ignoring invalid config field: " + field + ": " + parsedField);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
var metadata = parsedField[0];
|
|
134
|
+
try { // @ts-ignore
|
|
135
|
+
var config = JSON.parse(configs[i]);
|
|
136
|
+
result.set(metadata, config);
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
_this.log.error("Ignoring invalid config: " + configs[i]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return Promise.all(configKeys.map(function (configKey) { return _this.wrapper.del(configKey); })).then(function () { return result; });
|
|
143
|
+
}) :
|
|
144
|
+
// If configKeys is empty, return an empty map.
|
|
145
|
+
new _Map();
|
|
146
|
+
});
|
|
147
|
+
};
|
|
22
148
|
return TelemetryCachePluggable;
|
|
23
149
|
}());
|
|
24
150
|
export { TelemetryCachePluggable };
|
|
@@ -11,6 +11,8 @@ import { CONSUMER_PARTIAL_MODE, STORAGE_PLUGGABLE } from '../../utils/constants'
|
|
|
11
11
|
import { ImpressionsCacheInMemory } from '../inMemory/ImpressionsCacheInMemory';
|
|
12
12
|
import { EventsCacheInMemory } from '../inMemory/EventsCacheInMemory';
|
|
13
13
|
import { ImpressionCountsCacheInMemory } from '../inMemory/ImpressionCountsCacheInMemory';
|
|
14
|
+
import { shouldRecordTelemetry, TelemetryCacheInMemory } from '../inMemory/TelemetryCacheInMemory';
|
|
15
|
+
import { TelemetryCachePluggable } from './TelemetryCachePluggable';
|
|
14
16
|
var NO_VALID_WRAPPER = 'Expecting pluggable storage `wrapper` in options, but no valid wrapper instance was provided.';
|
|
15
17
|
var NO_VALID_WRAPPER_INTERFACE = 'The provided wrapper instance doesn’t follow the expected interface. Check our docs.';
|
|
16
18
|
/**
|
|
@@ -27,15 +29,6 @@ function validatePluggableStorageOptions(options) {
|
|
|
27
29
|
if (missingMethods.length)
|
|
28
30
|
throw new Error(NO_VALID_WRAPPER_INTERFACE + " The following methods are missing or invalid: " + missingMethods);
|
|
29
31
|
}
|
|
30
|
-
// subscription to wrapper connect event in order to emit SDK_READY event
|
|
31
|
-
function wrapperConnect(wrapper, onReadyCb) {
|
|
32
|
-
wrapper.connect().then(function () {
|
|
33
|
-
onReadyCb();
|
|
34
|
-
// At the moment, we don't synchronize config with pluggable storage
|
|
35
|
-
}).catch(function (e) {
|
|
36
|
-
onReadyCb(e || new Error('Error connecting wrapper'));
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
32
|
// Async return type in `client.track` method on consumer partial mode
|
|
40
33
|
// No need to promisify impressions cache
|
|
41
34
|
function promisifyEventsTrack(events) {
|
|
@@ -51,28 +44,39 @@ function promisifyEventsTrack(events) {
|
|
|
51
44
|
export function PluggableStorage(options) {
|
|
52
45
|
validatePluggableStorageOptions(options);
|
|
53
46
|
var prefix = validatePrefix(options.prefix);
|
|
54
|
-
function PluggableStorageFactory(
|
|
55
|
-
var log =
|
|
47
|
+
function PluggableStorageFactory(params) {
|
|
48
|
+
var log = params.log, metadata = params.metadata, onReadyCb = params.onReadyCb, mode = params.mode, eventsQueueSize = params.eventsQueueSize, impressionsQueueSize = params.impressionsQueueSize, optimize = params.optimize;
|
|
56
49
|
var keys = new KeyBuilderSS(prefix, metadata);
|
|
57
50
|
var wrapper = wrapperAdapter(log, options.wrapper);
|
|
58
51
|
var isPartialConsumer = mode === CONSUMER_PARTIAL_MODE;
|
|
52
|
+
var telemetry = shouldRecordTelemetry(params) ?
|
|
53
|
+
isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper) :
|
|
54
|
+
undefined;
|
|
59
55
|
// Connects to wrapper and emits SDK_READY event on main client
|
|
60
|
-
|
|
56
|
+
var connectPromise = wrapper.connect().then(function () {
|
|
57
|
+
onReadyCb();
|
|
58
|
+
// If mode is not defined, it means that the synchronizer is running and so we don't have to record telemetry
|
|
59
|
+
if (telemetry && telemetry.recordConfig && mode)
|
|
60
|
+
telemetry.recordConfig();
|
|
61
|
+
}).catch(function (e) {
|
|
62
|
+
e = e || new Error('Error connecting wrapper');
|
|
63
|
+
onReadyCb(e);
|
|
64
|
+
return e;
|
|
65
|
+
});
|
|
61
66
|
return {
|
|
62
67
|
splits: new SplitsCachePluggable(log, keys, wrapper),
|
|
63
68
|
segments: new SegmentsCachePluggable(log, keys, wrapper),
|
|
64
69
|
impressions: isPartialConsumer ? new ImpressionsCacheInMemory(impressionsQueueSize) : new ImpressionsCachePluggable(log, keys.buildImpressionsKey(), wrapper, metadata),
|
|
65
70
|
impressionCounts: optimize ? new ImpressionCountsCacheInMemory() : undefined,
|
|
66
71
|
events: isPartialConsumer ? promisifyEventsTrack(new EventsCacheInMemory(eventsQueueSize)) : new EventsCachePluggable(log, keys.buildEventsKey(), wrapper, metadata),
|
|
67
|
-
|
|
68
|
-
// telemetry: isPartialConsumer ? new TelemetryCacheInMemory() : new TelemetryCachePluggable(log, keys, wrapper),
|
|
72
|
+
telemetry: telemetry,
|
|
69
73
|
// Disconnect the underlying storage
|
|
70
74
|
destroy: function () {
|
|
71
75
|
return wrapper.disconnect();
|
|
72
76
|
},
|
|
73
77
|
// emits SDK_READY event on shared clients and returns a reference to the storage
|
|
74
78
|
shared: function (_, onReadyCb) {
|
|
75
|
-
|
|
79
|
+
connectPromise.then(onReadyCb);
|
|
76
80
|
return __assign(__assign({}, this), {
|
|
77
81
|
// no-op destroy, to disconnect the wrapper only when the main client is destroyed
|
|
78
82
|
destroy: function () { } });
|
|
@@ -2,7 +2,6 @@ import { eventsSubmitterFactory } from './eventsSubmitter';
|
|
|
2
2
|
import { impressionsSubmitterFactory } from './impressionsSubmitter';
|
|
3
3
|
import { impressionCountsSubmitterFactory } from './impressionCountsSubmitter';
|
|
4
4
|
import { telemetrySubmitterFactory } from './telemetrySubmitter';
|
|
5
|
-
import { uniqueKeysSubmitterFactory } from './uniqueKeysSubmitter';
|
|
6
5
|
export function submitterManagerFactory(params) {
|
|
7
6
|
var submitters = [
|
|
8
7
|
impressionsSubmitterFactory(params),
|
|
@@ -12,8 +11,6 @@ export function submitterManagerFactory(params) {
|
|
|
12
11
|
if (impressionCountsSubmitter)
|
|
13
12
|
submitters.push(impressionCountsSubmitter);
|
|
14
13
|
var telemetrySubmitter = telemetrySubmitterFactory(params);
|
|
15
|
-
if (params.uniqueKeysTracker)
|
|
16
|
-
submitters.push(uniqueKeysSubmitterFactory(params));
|
|
17
14
|
return {
|
|
18
15
|
// `onlyTelemetry` true if SDK is created with userConsent not GRANTED
|
|
19
16
|
start: function (onlyTelemetry) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
var _a, _b, _c;
|
|
2
2
|
import { submitterFactory, firstPushWindowDecorator } from './submitter';
|
|
3
|
-
import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG,
|
|
3
|
+
import { QUEUED, DEDUPED, DROPPED, CONSUMER_MODE, CONSUMER_ENUM, STANDALONE_MODE, CONSUMER_PARTIAL_MODE, STANDALONE_ENUM, CONSUMER_PARTIAL_ENUM, OPTIMIZED, DEBUG, DEBUG_ENUM, OPTIMIZED_ENUM, CONSENT_GRANTED, CONSENT_DECLINED, CONSENT_UNKNOWN } from '../../utils/constants';
|
|
4
4
|
import { SDK_READY, SDK_READY_FROM_CACHE } from '../../readiness/constants';
|
|
5
5
|
import { base } from '../../utils/settingsValidation';
|
|
6
6
|
import { usedKeysMap } from '../../utils/inputValidation/apiKey';
|
|
7
7
|
import { timer } from '../../utils/timeTracker/timer';
|
|
8
8
|
import { objectAssign } from '../../utils/lang/objectAssign';
|
|
9
|
+
import { isStorageSync } from '../../trackers/impressionObserver/utils';
|
|
9
10
|
/**
|
|
10
11
|
* Converts data from telemetry cache into /metrics/usage request payload.
|
|
11
12
|
*/
|
|
@@ -26,9 +27,9 @@ export function telemetryCacheStatsAdapter(telemetry, splits, segments) {
|
|
|
26
27
|
iQ: telemetry.getImpressionStats(QUEUED),
|
|
27
28
|
iDe: telemetry.getImpressionStats(DEDUPED),
|
|
28
29
|
iDr: telemetry.getImpressionStats(DROPPED),
|
|
29
|
-
spC: splits.getSplitNames().length,
|
|
30
|
-
seC: segments.getRegisteredSegments().length,
|
|
31
|
-
skC: segments.getKeysCount(),
|
|
30
|
+
spC: splits && splits.getSplitNames().length,
|
|
31
|
+
seC: segments && segments.getRegisteredSegments().length,
|
|
32
|
+
skC: segments && segments.getKeysCount(),
|
|
32
33
|
sL: telemetry.getSessionLength(),
|
|
33
34
|
eQ: telemetry.getEventStats(QUEUED),
|
|
34
35
|
eD: telemetry.getEventStats(DROPPED),
|
|
@@ -46,7 +47,6 @@ var OPERATION_MODE_MAP = (_a = {},
|
|
|
46
47
|
var IMPRESSIONS_MODE_MAP = (_b = {},
|
|
47
48
|
_b[OPTIMIZED] = OPTIMIZED_ENUM,
|
|
48
49
|
_b[DEBUG] = DEBUG_ENUM,
|
|
49
|
-
_b[NONE] = NONE_ENUM,
|
|
50
50
|
_b);
|
|
51
51
|
var USER_CONSENT_MAP = (_c = {},
|
|
52
52
|
_c[CONSENT_UNKNOWN] = 1,
|
|
@@ -120,7 +120,9 @@ export function telemetrySubmitterFactory(params) {
|
|
|
120
120
|
return; // No submitter created if telemetry cache is not defined
|
|
121
121
|
var settings = params.settings, _b = params.settings, log = _b.log, telemetryRefreshRate = _b.scheduler.telemetryRefreshRate, splitApi = params.splitApi, readiness = params.readiness, sdkReadinessManager = params.sdkReadinessManager;
|
|
122
122
|
var startTime = timer(now);
|
|
123
|
-
var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage,
|
|
123
|
+
var submitter = firstPushWindowDecorator(submitterFactory(log, splitApi.postMetricsUsage,
|
|
124
|
+
// @TODO cannot provide splits and segments cache if they are async, because `submitterFactory` expects a sync storage source
|
|
125
|
+
isStorageSync(params.settings) ? telemetryCacheStatsAdapter(telemetry, splits, segments) : telemetryCacheStatsAdapter(telemetry), telemetryRefreshRate, 'telemetry stats', undefined, 0, true), telemetryRefreshRate);
|
|
124
126
|
readiness.gate.once(SDK_READY_FROM_CACHE, function () {
|
|
125
127
|
telemetry.recordTimeUntilReadyFromCache(startTime());
|
|
126
128
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
|
+
import { truncateTimeFrame } from '../utils/time';
|
|
3
4
|
import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIONS_LISTENER } from '../logger/constants';
|
|
4
5
|
import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
|
|
5
6
|
/**
|
|
@@ -9,34 +10,52 @@ import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
|
|
|
9
10
|
* @param metadata runtime metadata (ip, hostname and version)
|
|
10
11
|
* @param impressionListener optional impression listener
|
|
11
12
|
* @param integrationsManager optional integrations manager
|
|
12
|
-
* @param
|
|
13
|
+
* @param observer optional impression observer. If provided, previous time (pt property) is included in impression instances
|
|
14
|
+
* @param countsCache optional cache to save impressions count. If provided, impressions will be deduped (OPTIMIZED mode)
|
|
13
15
|
*/
|
|
14
|
-
export function impressionsTrackerFactory(settings, impressionsCache,
|
|
16
|
+
export function impressionsTrackerFactory(settings, impressionsCache, integrationsManager,
|
|
17
|
+
// if observer is provided, it implies `shouldAddPreviousTime` flag (i.e., if impressions previous time should be added or not)
|
|
18
|
+
observer,
|
|
19
|
+
// if countsCache is provided, it implies `isOptimized` flag (i.e., if impressions should be deduped or not)
|
|
20
|
+
countsCache, telemetryCache) {
|
|
15
21
|
var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
|
|
16
22
|
return {
|
|
17
23
|
track: function (impressions, attributes) {
|
|
18
24
|
if (settings.userConsent === CONSENT_DECLINED)
|
|
19
25
|
return;
|
|
20
26
|
var impressionsCount = impressions.length;
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
res.then(function () {
|
|
28
|
-
log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
|
|
29
|
-
}).catch(function (err) {
|
|
30
|
-
log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
|
|
31
|
-
});
|
|
27
|
+
var impressionsToStore = []; // Track only the impressions that are going to be stored
|
|
28
|
+
// Wraps impressions to store and adds previousTime if it corresponds
|
|
29
|
+
impressions.forEach(function (impression) {
|
|
30
|
+
if (observer) {
|
|
31
|
+
// Adds previous time if it is enabled
|
|
32
|
+
impression.pt = observer.testAndSet(impression);
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
var now = Date.now();
|
|
35
|
+
if (countsCache) {
|
|
36
|
+
// Increments impression counter per featureName
|
|
37
|
+
countsCache.track(impression.feature, now, 1);
|
|
38
|
+
}
|
|
39
|
+
// Checks if the impression should be added in queue to be sent
|
|
40
|
+
if (!countsCache || !impression.pt || impression.pt < truncateTimeFrame(now)) {
|
|
41
|
+
impressionsToStore.push(impression);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
var res = impressionsCache.track(impressionsToStore);
|
|
45
|
+
// If we're on an async storage, handle error and log it.
|
|
46
|
+
if (thenable(res)) {
|
|
47
|
+
res.then(function () {
|
|
48
|
+
log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
|
|
49
|
+
}).catch(function (err) {
|
|
50
|
+
log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Record when impressionsCache is sync only (standalone mode)
|
|
55
|
+
// @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
|
|
56
|
+
if (telemetryCache) {
|
|
57
|
+
telemetryCache.recordImpressionStats(QUEUED, impressionsToStore.length);
|
|
58
|
+
telemetryCache.recordImpressionStats(DEDUPED, impressions.length - impressionsToStore.length);
|
|
40
59
|
}
|
|
41
60
|
}
|
|
42
61
|
// @TODO next block might be handled by the integration manager. In that case, the metadata object doesn't need to be passed in the constructor
|
|
@@ -44,7 +63,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, strategy,
|
|
|
44
63
|
var _loop_1 = function (i) {
|
|
45
64
|
var impressionData = {
|
|
46
65
|
// copy of impression, to avoid unexpected behaviour if modified by integrations or impressionListener
|
|
47
|
-
impression: objectAssign({},
|
|
66
|
+
impression: objectAssign({}, impressions[i]),
|
|
48
67
|
attributes: attributes,
|
|
49
68
|
ip: ip,
|
|
50
69
|
hostname: hostname,
|
|
@@ -64,7 +83,7 @@ export function impressionsTrackerFactory(settings, impressionsCache, strategy,
|
|
|
64
83
|
}
|
|
65
84
|
}, 0);
|
|
66
85
|
};
|
|
67
|
-
for (var i = 0; i <
|
|
86
|
+
for (var i = 0; i < impressionsCount; i++) {
|
|
68
87
|
_loop_1(i);
|
|
69
88
|
}
|
|
70
89
|
}
|
|
@@ -13,7 +13,6 @@ export var SPLIT_EVENT = 'EVENT';
|
|
|
13
13
|
// Impression collection modes
|
|
14
14
|
export var DEBUG = 'DEBUG';
|
|
15
15
|
export var OPTIMIZED = 'OPTIMIZED';
|
|
16
|
-
export var NONE = 'NONE';
|
|
17
16
|
// SDK Modes
|
|
18
17
|
export var LOCALHOST_MODE = 'localhost';
|
|
19
18
|
export var STANDALONE_MODE = 'standalone';
|
|
@@ -38,7 +37,6 @@ export var CONSUMER_ENUM = 1;
|
|
|
38
37
|
export var CONSUMER_PARTIAL_ENUM = 2;
|
|
39
38
|
export var OPTIMIZED_ENUM = 0;
|
|
40
39
|
export var DEBUG_ENUM = 1;
|
|
41
|
-
export var NONE_ENUM = 2;
|
|
42
40
|
export var SPLITS = 'sp';
|
|
43
41
|
export var IMPRESSIONS = 'im';
|
|
44
42
|
export var IMPRESSIONS_COUNT = 'ic';
|
package/esm/utils/lang/maps.js
CHANGED
|
@@ -37,13 +37,6 @@ var MapPoly = /** @class */ (function () {
|
|
|
37
37
|
this.__mapKeysData__.length = 0;
|
|
38
38
|
this.__mapValuesData__.length = 0;
|
|
39
39
|
};
|
|
40
|
-
MapPoly.prototype.set = function (key, value) {
|
|
41
|
-
var index = this.__mapKeysData__.indexOf(key);
|
|
42
|
-
if (index === -1)
|
|
43
|
-
index = this.__mapKeysData__.push(key) - 1;
|
|
44
|
-
this.__mapValuesData__[index] = value;
|
|
45
|
-
return this;
|
|
46
|
-
};
|
|
47
40
|
MapPoly.prototype.delete = function (key) {
|
|
48
41
|
var index = this.__mapKeysData__.indexOf(key);
|
|
49
42
|
if (index === -1)
|
|
@@ -52,12 +45,27 @@ var MapPoly = /** @class */ (function () {
|
|
|
52
45
|
this.__mapValuesData__.splice(index, 1);
|
|
53
46
|
return true;
|
|
54
47
|
};
|
|
48
|
+
MapPoly.prototype.forEach = function (callbackfn, thisArg) {
|
|
49
|
+
for (var i = 0; i < this.__mapKeysData__.length; i++) {
|
|
50
|
+
callbackfn.call(thisArg, this.__mapValuesData__[i], this.__mapKeysData__[i], this);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
55
53
|
MapPoly.prototype.get = function (key) {
|
|
56
54
|
var index = this.__mapKeysData__.indexOf(key);
|
|
57
55
|
if (index === -1)
|
|
58
56
|
return;
|
|
59
57
|
return this.__mapValuesData__[index];
|
|
60
58
|
};
|
|
59
|
+
MapPoly.prototype.has = function (key) {
|
|
60
|
+
return this.__mapKeysData__.indexOf(key) !== -1;
|
|
61
|
+
};
|
|
62
|
+
MapPoly.prototype.set = function (key, value) {
|
|
63
|
+
var index = this.__mapKeysData__.indexOf(key);
|
|
64
|
+
if (index === -1)
|
|
65
|
+
index = this.__mapKeysData__.push(key) - 1;
|
|
66
|
+
this.__mapValuesData__[index] = value;
|
|
67
|
+
return this;
|
|
68
|
+
};
|
|
61
69
|
Object.defineProperty(MapPoly.prototype, "size", {
|
|
62
70
|
get: function () {
|
|
63
71
|
return this.__mapKeysData__.length;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ERROR_INVALID_CONFIG_PARAM } from '../../logger/constants';
|
|
2
|
-
import { DEBUG, OPTIMIZED
|
|
2
|
+
import { DEBUG, OPTIMIZED } from '../constants';
|
|
3
3
|
import { stringToUpperCase } from '../lang';
|
|
4
4
|
export function validImpressionsMode(log, impressionsMode) {
|
|
5
5
|
impressionsMode = stringToUpperCase(impressionsMode);
|
|
6
|
-
if ([DEBUG, OPTIMIZED
|
|
6
|
+
if ([DEBUG, OPTIMIZED].indexOf(impressionsMode) > -1)
|
|
7
7
|
return impressionsMode;
|
|
8
|
-
log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED
|
|
8
|
+
log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED], OPTIMIZED]);
|
|
9
9
|
return OPTIMIZED;
|
|
10
10
|
}
|
|
@@ -31,8 +31,6 @@ export var base = {
|
|
|
31
31
|
telemetryRefreshRate: 3600,
|
|
32
32
|
// publish evaluations each 300 sec (default value for OPTIMIZED impressions mode)
|
|
33
33
|
impressionsRefreshRate: 300,
|
|
34
|
-
// publish unique Keys each 900 sec (15 min)
|
|
35
|
-
uniqueKeysRefreshRate: 900,
|
|
36
34
|
// fetch offline changes each 15 sec
|
|
37
35
|
offlineRefreshRate: 15,
|
|
38
36
|
// publish events every 60 seconds after the first flush
|
|
@@ -111,8 +109,6 @@ export function settingsValidation(config, validationParams) {
|
|
|
111
109
|
scheduler.segmentsRefreshRate = fromSecondsToMillis(scheduler.segmentsRefreshRate);
|
|
112
110
|
scheduler.offlineRefreshRate = fromSecondsToMillis(scheduler.offlineRefreshRate);
|
|
113
111
|
scheduler.eventsPushRate = fromSecondsToMillis(scheduler.eventsPushRate);
|
|
114
|
-
scheduler.uniqueKeysRefreshRate = fromSecondsToMillis(scheduler.uniqueKeysRefreshRate);
|
|
115
|
-
scheduler.impressionCountsRefreshRate = fromSecondsToMillis(scheduler.impressionCountsRefreshRate);
|
|
116
112
|
scheduler.telemetryRefreshRate = fromSecondsToMillis(validateMinValue('telemetryRefreshRate', scheduler.telemetryRefreshRate, 60));
|
|
117
113
|
// Default impressionsRefreshRate for DEBUG mode is 60 secs
|
|
118
114
|
if (get(config, 'scheduler.impressionsRefreshRate') === undefined && withDefaults.sync.impressionsMode === DEBUG)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "1.6.2-rc.
|
|
3
|
+
"version": "1.6.2-rc.9",
|
|
4
4
|
"description": "Split Javascript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"build:cjs": "rimraf cjs && tsc -m CommonJS --outDir cjs",
|
|
25
25
|
"test": "jest",
|
|
26
26
|
"test:coverage": "jest --coverage",
|
|
27
|
+
"all": "npm run check && npm run build && npm run test",
|
|
27
28
|
"publish:rc": "npm run check && npm run test && npm run build && npm publish --tag rc",
|
|
28
29
|
"publish:stable": "npm run check && npm run test && npm run build && npm publish"
|
|
29
30
|
},
|
|
@@ -40,8 +40,8 @@ export function createUserConsentAPI(params: ISdkFactoryContext) {
|
|
|
40
40
|
|
|
41
41
|
// @ts-ignore, clear method is present in storage for standalone and partial consumer mode
|
|
42
42
|
if (events.clear) events.clear(); // @ts-ignore
|
|
43
|
-
if (impressions.clear) impressions.clear()
|
|
44
|
-
if (impressionCounts
|
|
43
|
+
if (impressions.clear) impressions.clear();
|
|
44
|
+
if (impressionCounts) impressionCounts.clear();
|
|
45
45
|
}
|
|
46
46
|
} else {
|
|
47
47
|
log.info(USER_CONSENT_NOT_UPDATED, [newConsentStatus]);
|
package/src/evaluator/index.ts
CHANGED
|
@@ -29,19 +29,19 @@ export function evaluateFeature(
|
|
|
29
29
|
attributes: SplitIO.Attributes | undefined,
|
|
30
30
|
storage: IStorageSync | IStorageAsync,
|
|
31
31
|
): MaybeThenable<IEvaluationResult> {
|
|
32
|
-
let
|
|
32
|
+
let parsedSplit;
|
|
33
33
|
|
|
34
34
|
try {
|
|
35
|
-
|
|
35
|
+
parsedSplit = storage.splits.getSplit(splitName);
|
|
36
36
|
} catch (e) {
|
|
37
37
|
// Exception on sync `getSplit` storage. Not possible ATM with InMemory and InLocal storages.
|
|
38
38
|
return treatmentException;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
if (thenable(
|
|
42
|
-
return
|
|
41
|
+
if (thenable(parsedSplit)) {
|
|
42
|
+
return parsedSplit.then((split) => getEvaluation(
|
|
43
43
|
log,
|
|
44
|
-
|
|
44
|
+
split,
|
|
45
45
|
key,
|
|
46
46
|
attributes,
|
|
47
47
|
storage,
|
|
@@ -54,7 +54,7 @@ export function evaluateFeature(
|
|
|
54
54
|
|
|
55
55
|
return getEvaluation(
|
|
56
56
|
log,
|
|
57
|
-
|
|
57
|
+
parsedSplit,
|
|
58
58
|
key,
|
|
59
59
|
attributes,
|
|
60
60
|
storage,
|
package/src/listeners/browser.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { fromImpressionCountsCollector } from '../sync/submitters/impressionCoun
|
|
|
7
7
|
import { IResponse, ISplitApi } from '../services/types';
|
|
8
8
|
import { ImpressionDTO, ISettings } from '../types';
|
|
9
9
|
import { ImpressionsPayload } from '../sync/submitters/types';
|
|
10
|
-
import { OPTIMIZED, DEBUG
|
|
10
|
+
import { OPTIMIZED, DEBUG } from '../utils/constants';
|
|
11
11
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
12
12
|
import { CLEANUP_REGISTERING, CLEANUP_DEREGISTERING } from '../logger/constants';
|
|
13
13
|
import { ISyncManager } from '../sync/types';
|
|
@@ -88,10 +88,9 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
88
88
|
// Flush impressions & events data if there is user consent
|
|
89
89
|
if (isConsentGranted(this.settings)) {
|
|
90
90
|
const eventsUrl = this.settings.urls.events;
|
|
91
|
-
const sim = this.settings.sync.impressionsMode;
|
|
92
91
|
const extraMetadata = {
|
|
93
92
|
// sim stands for Sync/Split Impressions Mode
|
|
94
|
-
sim:
|
|
93
|
+
sim: this.settings.sync.impressionsMode === OPTIMIZED ? OPTIMIZED : DEBUG
|
|
95
94
|
};
|
|
96
95
|
|
|
97
96
|
this._flushData(eventsUrl + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);
|
|
Binary file
|
package/src/logger/constants.ts
CHANGED
|
@@ -143,5 +143,4 @@ export const LOG_PREFIX_SYNC_POLLING = LOG_PREFIX_SYNC + ':polling-manager: ';
|
|
|
143
143
|
export const LOG_PREFIX_SYNC_SUBMITTERS = LOG_PREFIX_SYNC + ':submitter: ';
|
|
144
144
|
export const LOG_PREFIX_IMPRESSIONS_TRACKER = 'impressions-tracker: ';
|
|
145
145
|
export const LOG_PREFIX_EVENTS_TRACKER = 'events-tracker: ';
|
|
146
|
-
export const LOG_PREFIX_UNIQUE_KEYS_TRACKER = 'unique-keys-tracker: ';
|
|
147
146
|
export const LOG_PREFIX_CLEANUP = 'cleanup: ';
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -9,6 +9,17 @@ import { IEvaluationResult } from '../evaluator/types';
|
|
|
9
9
|
import { SplitIO, ImpressionDTO } from '../types';
|
|
10
10
|
import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants';
|
|
11
11
|
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
12
|
+
import { isStorageSync } from '../trackers/impressionObserver/utils';
|
|
13
|
+
|
|
14
|
+
const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY };
|
|
15
|
+
|
|
16
|
+
function treatmentsNotReady(splitNames: string[]) {
|
|
17
|
+
const evaluations: Record<string, IEvaluationResult> = {};
|
|
18
|
+
splitNames.forEach(splitName => {
|
|
19
|
+
evaluations[splitName] = treatmentNotReady;
|
|
20
|
+
});
|
|
21
|
+
return evaluations;
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
/**
|
|
14
25
|
* Creator of base client with getTreatments and track methods.
|
|
@@ -29,7 +40,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
29
40
|
return treatment;
|
|
30
41
|
};
|
|
31
42
|
|
|
32
|
-
const evaluation =
|
|
43
|
+
const evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
44
|
+
evaluateFeature(log, key, splitName, attributes, storage) :
|
|
45
|
+
isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
|
|
46
|
+
treatmentNotReady :
|
|
47
|
+
Promise.resolve(treatmentNotReady); // Promisify if async
|
|
33
48
|
|
|
34
49
|
return thenable(evaluation) ? evaluation.then((res) => wrapUp(res)) : wrapUp(evaluation);
|
|
35
50
|
}
|
|
@@ -53,7 +68,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
53
68
|
return treatments;
|
|
54
69
|
};
|
|
55
70
|
|
|
56
|
-
const evaluations =
|
|
71
|
+
const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ?
|
|
72
|
+
evaluateFeatures(log, key, splitNames, attributes, storage) :
|
|
73
|
+
isStorageSync(settings) ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected
|
|
74
|
+
treatmentsNotReady(splitNames) :
|
|
75
|
+
Promise.resolve(treatmentsNotReady(splitNames)); // Promisify if async
|
|
57
76
|
|
|
58
77
|
return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations);
|
|
59
78
|
}
|
|
@@ -72,15 +91,9 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
|
|
|
72
91
|
invokingMethodName: string,
|
|
73
92
|
queue: ImpressionDTO[]
|
|
74
93
|
): SplitIO.Treatment | SplitIO.TreatmentWithConfig {
|
|
75
|
-
const isSdkReady = readinessManager.isReady() || readinessManager.isReadyFromCache();
|
|
76
94
|
const matchingKey = getMatching(key);
|
|
77
95
|
const bucketingKey = getBucketing(key);
|
|
78
96
|
|
|
79
|
-
// If the SDK was not ready, treatment may be incorrect due to having Splits but not segments data.
|
|
80
|
-
if (!isSdkReady) {
|
|
81
|
-
evaluation = { treatment: CONTROL, label: SDK_NOT_READY };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
97
|
const { treatment, label, changeNumber, config = null } = evaluation;
|
|
85
98
|
log.info(IMPRESSION, [splitName, matchingKey, treatment, label]);
|
|
86
99
|
|
|
@@ -9,7 +9,7 @@ import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
|
9
9
|
* Creates an Sdk client, i.e., a base client with status and destroy interface
|
|
10
10
|
*/
|
|
11
11
|
export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: boolean): SplitIO.IClient | SplitIO.IAsyncClient {
|
|
12
|
-
const { sdkReadinessManager, syncManager, storage, signalListener, settings, telemetryTracker
|
|
12
|
+
const { sdkReadinessManager, syncManager, storage, signalListener, settings, telemetryTracker } = params;
|
|
13
13
|
|
|
14
14
|
return objectAssign(
|
|
15
15
|
// Proto-linkage of the readiness Event Emitter
|
|
@@ -39,8 +39,6 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
|
|
|
39
39
|
|
|
40
40
|
// Release the API Key if it is the main client
|
|
41
41
|
if (!isSharedClient) releaseApiKey(settings.core.authorizationKey);
|
|
42
|
-
|
|
43
|
-
if (uniqueKeysTracker) uniqueKeysTracker.stop();
|
|
44
42
|
|
|
45
43
|
// Cleanup storage
|
|
46
44
|
return storage.destroy();
|