@splitsoftware/splitio-commons 1.6.2-rc.9 → 1.7.1
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 +6 -1
- package/cjs/consent/sdkUserConsent.js +2 -2
- package/cjs/listeners/browser.js +11 -12
- package/cjs/logger/constants.js +2 -1
- package/cjs/sdkClient/sdkClient.js +3 -1
- package/cjs/sdkFactory/index.js +26 -26
- package/cjs/services/splitApi.js +20 -0
- package/cjs/storages/AbstractSplitsCacheAsync.js +1 -1
- package/cjs/storages/AbstractSplitsCacheSync.js +1 -1
- package/cjs/storages/KeyBuilderSS.js +10 -43
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -1
- package/cjs/storages/inLocalStorage/index.js +17 -9
- package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +13 -6
- package/cjs/storages/inMemory/InMemoryStorageCS.js +13 -6
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +60 -35
- package/cjs/storages/inMemory/UniqueKeysCacheInMemory.js +72 -0
- package/cjs/storages/inMemory/UniqueKeysCacheInMemoryCS.js +76 -0
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +85 -0
- package/cjs/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +4 -4
- package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +71 -0
- package/cjs/storages/inRedis/constants.js +4 -1
- package/cjs/storages/inRedis/index.js +20 -3
- package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +81 -0
- package/cjs/storages/pluggable/ImpressionsCachePluggable.js +2 -19
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +4 -4
- package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +61 -0
- package/cjs/storages/pluggable/inMemoryWrapper.js +8 -6
- package/cjs/storages/pluggable/index.js +38 -9
- package/cjs/storages/utils.js +73 -0
- package/cjs/sync/submitters/submitterManager.js +3 -0
- package/cjs/sync/submitters/telemetrySubmitter.js +5 -40
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +27 -0
- package/cjs/trackers/impressionObserver/utils.js +1 -17
- package/cjs/trackers/impressionsTracker.js +22 -41
- package/cjs/trackers/strategy/strategyDebug.js +25 -0
- package/cjs/trackers/strategy/strategyNone.js +29 -0
- package/cjs/trackers/strategy/strategyOptimized.js +35 -0
- package/cjs/trackers/uniqueKeysTracker.js +38 -0
- package/cjs/utils/constants/index.js +4 -2
- package/cjs/utils/redis/RedisMock.js +31 -0
- package/cjs/utils/settingsValidation/impressionsMode.js +2 -2
- package/cjs/utils/settingsValidation/index.js +9 -3
- package/esm/consent/sdkUserConsent.js +2 -2
- package/esm/listeners/browser.js +12 -13
- package/esm/logger/constants.js +1 -0
- package/esm/sdkClient/sdkClient.js +3 -1
- package/esm/sdkFactory/index.js +26 -26
- package/esm/services/splitApi.js +20 -0
- package/esm/storages/AbstractSplitsCacheAsync.js +1 -1
- package/esm/storages/AbstractSplitsCacheSync.js +1 -1
- package/esm/storages/KeyBuilderSS.js +7 -37
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -1
- package/esm/storages/inLocalStorage/index.js +18 -10
- package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +12 -1
- package/esm/storages/inMemory/InMemoryStorage.js +14 -7
- package/esm/storages/inMemory/InMemoryStorageCS.js +14 -7
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +61 -36
- package/esm/storages/inMemory/UniqueKeysCacheInMemory.js +68 -0
- package/esm/storages/inMemory/UniqueKeysCacheInMemoryCS.js +73 -0
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +82 -0
- package/esm/storages/inRedis/ImpressionsCacheInRedis.js +2 -19
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +1 -1
- package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +68 -0
- package/esm/storages/inRedis/constants.js +3 -0
- package/esm/storages/inRedis/index.js +21 -4
- package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +78 -0
- package/esm/storages/pluggable/ImpressionsCachePluggable.js +2 -19
- package/esm/storages/pluggable/TelemetryCachePluggable.js +1 -1
- package/esm/storages/pluggable/UniqueKeysCachePluggable.js +58 -0
- package/esm/storages/pluggable/inMemoryWrapper.js +8 -6
- package/esm/storages/pluggable/index.js +39 -10
- package/esm/storages/utils.js +65 -0
- package/esm/sync/submitters/submitterManager.js +3 -0
- package/esm/sync/submitters/telemetrySubmitter.js +5 -39
- package/esm/sync/submitters/uniqueKeysSubmitter.js +23 -0
- package/esm/trackers/impressionObserver/utils.js +1 -15
- package/esm/trackers/impressionsTracker.js +22 -41
- package/esm/trackers/strategy/strategyDebug.js +21 -0
- package/esm/trackers/strategy/strategyNone.js +25 -0
- package/esm/trackers/strategy/strategyOptimized.js +31 -0
- package/esm/trackers/uniqueKeysTracker.js +34 -0
- package/esm/utils/constants/index.js +2 -0
- package/esm/utils/redis/RedisMock.js +28 -0
- package/esm/utils/settingsValidation/impressionsMode.js +3 -3
- package/esm/utils/settingsValidation/index.js +9 -3
- package/package.json +1 -2
- package/src/consent/sdkUserConsent.ts +2 -2
- package/src/listeners/browser.ts +12 -15
- package/src/logger/constants.ts +1 -0
- package/src/sdkClient/sdkClient.ts +3 -1
- package/src/sdkFactory/index.ts +29 -31
- package/src/sdkFactory/types.ts +7 -4
- package/src/services/splitApi.ts +22 -0
- package/src/services/types.ts +6 -0
- package/src/storages/AbstractSplitsCacheAsync.ts +1 -1
- package/src/storages/AbstractSplitsCacheSync.ts +1 -1
- package/src/storages/KeyBuilderSS.ts +9 -43
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +0 -1
- package/src/storages/inLocalStorage/index.ts +18 -10
- package/src/storages/inMemory/AttributesCacheInMemory.ts +7 -7
- package/src/storages/inMemory/ImpressionCountsCacheInMemory.ts +16 -1
- package/src/storages/inMemory/InMemoryStorage.ts +14 -7
- package/src/storages/inMemory/InMemoryStorageCS.ts +14 -7
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +69 -34
- package/src/storages/inMemory/UniqueKeysCacheInMemory.ts +80 -0
- package/src/storages/inMemory/UniqueKeysCacheInMemoryCS.ts +86 -0
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +95 -0
- package/src/storages/inRedis/ImpressionsCacheInRedis.ts +2 -22
- package/src/storages/inRedis/TelemetryCacheInRedis.ts +3 -2
- package/src/storages/inRedis/UniqueKeysCacheInRedis.ts +77 -0
- package/src/storages/inRedis/constants.ts +3 -0
- package/src/storages/inRedis/index.ts +18 -5
- package/src/storages/pluggable/ImpressionCountsCachePluggable.ts +92 -0
- package/src/storages/pluggable/ImpressionsCachePluggable.ts +3 -23
- package/src/storages/pluggable/TelemetryCachePluggable.ts +3 -2
- package/src/storages/pluggable/UniqueKeysCachePluggable.ts +67 -0
- package/src/storages/pluggable/inMemoryWrapper.ts +6 -6
- package/src/storages/pluggable/index.ts +41 -9
- package/src/storages/types.ts +56 -55
- package/src/storages/utils.ts +78 -0
- package/src/sync/submitters/submitter.ts +2 -2
- package/src/sync/submitters/submitterManager.ts +2 -0
- package/src/sync/submitters/telemetrySubmitter.ts +8 -43
- package/src/sync/submitters/types.ts +29 -27
- package/src/sync/submitters/uniqueKeysSubmitter.ts +36 -0
- package/src/trackers/impressionObserver/utils.ts +1 -16
- package/src/trackers/impressionsTracker.ts +25 -46
- package/src/trackers/strategy/strategyDebug.ts +28 -0
- package/src/trackers/strategy/strategyNone.ts +34 -0
- package/src/trackers/strategy/strategyOptimized.ts +42 -0
- package/src/trackers/types.ts +28 -0
- package/src/trackers/uniqueKeysTracker.ts +48 -0
- package/src/types.ts +1 -1
- package/src/utils/constants/index.ts +2 -0
- package/src/utils/redis/RedisMock.ts +33 -0
- package/src/utils/settingsValidation/impressionsMode.ts +3 -3
- package/src/utils/settingsValidation/index.ts +7 -3
- package/types/logger/constants.d.ts +1 -0
- package/types/sdkFactory/types.d.ts +4 -2
- package/types/services/types.d.ts +4 -0
- package/types/storages/AbstractSplitsCacheAsync.d.ts +1 -1
- package/types/storages/AbstractSplitsCacheSync.d.ts +1 -1
- package/types/storages/KeyBuilderSS.d.ts +3 -3
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +0 -1
- package/types/storages/inMemory/ImpressionCountsCacheInMemory.d.ts +5 -1
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +20 -9
- package/types/storages/inMemory/uniqueKeysCacheInMemory.d.ts +35 -0
- package/types/storages/inMemory/uniqueKeysCacheInMemoryCS.d.ts +35 -0
- package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +16 -0
- package/types/storages/inRedis/ImpressionsCacheInRedis.d.ts +0 -1
- package/types/storages/inRedis/constants.d.ts +3 -0
- package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +21 -0
- package/types/storages/pluggable/ImpressionCountsCachePluggable.d.ts +16 -0
- package/types/storages/pluggable/ImpressionsCachePluggable.d.ts +1 -2
- package/types/storages/pluggable/UniqueKeysCachePluggable.d.ts +20 -0
- package/types/storages/types.d.ts +39 -38
- package/types/storages/utils.d.ts +8 -0
- package/types/sync/submitters/submitter.d.ts +2 -2
- package/types/sync/submitters/telemetrySubmitter.d.ts +2 -10
- package/types/sync/submitters/types.d.ts +27 -7
- package/types/sync/submitters/uniqueKeysSubmitter.d.ts +5 -0
- package/types/trackers/impressionObserver/utils.d.ts +0 -8
- package/types/trackers/impressionsTracker.d.ts +4 -6
- package/types/trackers/strategy/strategyDebug.d.ts +9 -0
- package/types/trackers/strategy/strategyNone.d.ts +10 -0
- package/types/trackers/strategy/strategyOptimized.d.ts +11 -0
- package/types/trackers/types.d.ts +23 -0
- package/types/trackers/uniqueKeysTracker.d.ts +13 -0
- package/types/types.d.ts +1 -1
- package/types/utils/constants/index.d.ts +2 -0
- package/types/utils/redis/RedisMock.d.ts +4 -0
- package/cjs/storages/metadataBuilder.js +0 -12
- package/esm/storages/metadataBuilder.js +0 -8
- package/src/storages/metadataBuilder.ts +0 -11
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
|
-
import { truncateTimeFrame } from '../utils/time';
|
|
4
3
|
import { IMPRESSIONS_TRACKER_SUCCESS, ERROR_IMPRESSIONS_TRACKER, ERROR_IMPRESSIONS_LISTENER } from '../logger/constants';
|
|
5
4
|
import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
|
|
6
5
|
/**
|
|
@@ -10,52 +9,34 @@ import { CONSENT_DECLINED, DEDUPED, QUEUED } from '../utils/constants';
|
|
|
10
9
|
* @param metadata runtime metadata (ip, hostname and version)
|
|
11
10
|
* @param impressionListener optional impression listener
|
|
12
11
|
* @param integrationsManager optional integrations manager
|
|
13
|
-
* @param
|
|
14
|
-
* @param countsCache optional cache to save impressions count. If provided, impressions will be deduped (OPTIMIZED mode)
|
|
12
|
+
* @param strategy strategy for impressions tracking.
|
|
15
13
|
*/
|
|
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) {
|
|
14
|
+
export function impressionsTrackerFactory(settings, impressionsCache, strategy, integrationsManager, telemetryCache) {
|
|
21
15
|
var log = settings.log, impressionListener = settings.impressionListener, _a = settings.runtime, ip = _a.ip, hostname = _a.hostname, version = settings.version;
|
|
22
16
|
return {
|
|
23
17
|
track: function (impressions, attributes) {
|
|
24
18
|
if (settings.userConsent === CONSENT_DECLINED)
|
|
25
19
|
return;
|
|
26
20
|
var impressionsCount = impressions.length;
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
var _a = strategy.process(impressions), impressionsToStore = _a.impressionsToStore, impressionsToListener = _a.impressionsToListener, deduped = _a.deduped;
|
|
22
|
+
var impressionsToListenerCount = impressionsToListener.length;
|
|
23
|
+
if (impressionsToStore.length > 0) {
|
|
24
|
+
var res = impressionsCache.track(impressionsToStore);
|
|
25
|
+
// If we're on an async storage, handle error and log it.
|
|
26
|
+
if (thenable(res)) {
|
|
27
|
+
res.then(function () {
|
|
28
|
+
log.info(IMPRESSIONS_TRACKER_SUCCESS, [impressionsCount]);
|
|
29
|
+
}).catch(function (err) {
|
|
30
|
+
log.error(ERROR_IMPRESSIONS_TRACKER, [impressionsCount, err]);
|
|
31
|
+
});
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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);
|
|
33
|
+
else {
|
|
34
|
+
// Record when impressionsCache is sync only (standalone mode)
|
|
35
|
+
// @TODO we are not dropping impressions on full queue yet, so DROPPED stats are not recorded
|
|
36
|
+
if (telemetryCache) {
|
|
37
|
+
telemetryCache.recordImpressionStats(QUEUED, impressionsToStore.length);
|
|
38
|
+
telemetryCache.recordImpressionStats(DEDUPED, deduped);
|
|
39
|
+
}
|
|
59
40
|
}
|
|
60
41
|
}
|
|
61
42
|
// @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
|
|
@@ -63,7 +44,7 @@ countsCache, telemetryCache) {
|
|
|
63
44
|
var _loop_1 = function (i) {
|
|
64
45
|
var impressionData = {
|
|
65
46
|
// copy of impression, to avoid unexpected behaviour if modified by integrations or impressionListener
|
|
66
|
-
impression: objectAssign({},
|
|
47
|
+
impression: objectAssign({}, impressionsToListener[i]),
|
|
67
48
|
attributes: attributes,
|
|
68
49
|
ip: ip,
|
|
69
50
|
hostname: hostname,
|
|
@@ -83,7 +64,7 @@ countsCache, telemetryCache) {
|
|
|
83
64
|
}
|
|
84
65
|
}, 0);
|
|
85
66
|
};
|
|
86
|
-
for (var i = 0; i <
|
|
67
|
+
for (var i = 0; i < impressionsToListenerCount; i++) {
|
|
87
68
|
_loop_1(i);
|
|
88
69
|
}
|
|
89
70
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
|
|
3
|
+
*
|
|
4
|
+
* @param impressionsObserver impression observer. Previous time (pt property) is included in impression instances
|
|
5
|
+
* @returns IStrategyResult
|
|
6
|
+
*/
|
|
7
|
+
export function strategyDebugFactory(impressionsObserver) {
|
|
8
|
+
return {
|
|
9
|
+
process: function (impressions) {
|
|
10
|
+
impressions.forEach(function (impression) {
|
|
11
|
+
// Adds previous time if it is enabled
|
|
12
|
+
impression.pt = impressionsObserver.testAndSet(impression);
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
impressionsToStore: impressions,
|
|
16
|
+
impressionsToListener: impressions,
|
|
17
|
+
deduped: 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* None strategy for impressions tracker.
|
|
3
|
+
*
|
|
4
|
+
* @param impressionsCounter cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
|
|
5
|
+
* @param uniqueKeysTracker unique keys tracker in charge of tracking the unique keys per split.
|
|
6
|
+
* @returns IStrategyResult
|
|
7
|
+
*/
|
|
8
|
+
export function strategyNoneFactory(impressionsCounter, uniqueKeysTracker) {
|
|
9
|
+
return {
|
|
10
|
+
process: function (impressions) {
|
|
11
|
+
impressions.forEach(function (impression) {
|
|
12
|
+
var now = Date.now();
|
|
13
|
+
// Increments impression counter per featureName
|
|
14
|
+
impressionsCounter.track(impression.feature, now, 1);
|
|
15
|
+
// Keep track by unique key
|
|
16
|
+
uniqueKeysTracker.track(impression.keyName, impression.feature);
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
impressionsToStore: [],
|
|
20
|
+
impressionsToListener: impressions,
|
|
21
|
+
deduped: 0
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { truncateTimeFrame } from '../../utils/time';
|
|
2
|
+
/**
|
|
3
|
+
* Optimized strategy for impressions tracker. Wraps impressions to store and adds previousTime if it corresponds
|
|
4
|
+
*
|
|
5
|
+
* @param impressionsObserver impression observer. previous time (pt property) is included in impression instances
|
|
6
|
+
* @param impressionsCounter cache to save impressions count. impressions will be deduped (OPTIMIZED mode)
|
|
7
|
+
* @returns IStrategyResult
|
|
8
|
+
*/
|
|
9
|
+
export function strategyOptimizedFactory(impressionsObserver, impressionsCounter) {
|
|
10
|
+
return {
|
|
11
|
+
process: function (impressions) {
|
|
12
|
+
var impressionsToStore = [];
|
|
13
|
+
impressions.forEach(function (impression) {
|
|
14
|
+
impression.pt = impressionsObserver.testAndSet(impression);
|
|
15
|
+
var now = Date.now();
|
|
16
|
+
// Increments impression counter per featureName
|
|
17
|
+
if (impression.pt)
|
|
18
|
+
impressionsCounter.track(impression.feature, now, 1);
|
|
19
|
+
// Checks if the impression should be added in queue to be sent
|
|
20
|
+
if (!impression.pt || impression.pt < truncateTimeFrame(now)) {
|
|
21
|
+
impressionsToStore.push(impression);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
impressionsToStore: impressionsToStore,
|
|
26
|
+
impressionsToListener: impressions,
|
|
27
|
+
deduped: impressions.length - impressionsToStore.length
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { LOG_PREFIX_UNIQUE_KEYS_TRACKER } from '../logger/constants';
|
|
2
|
+
var noopFilterAdapter = {
|
|
3
|
+
add: function () { return true; },
|
|
4
|
+
contains: function () { return true; },
|
|
5
|
+
clear: function () { }
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Trackes uniques keys
|
|
9
|
+
* Unique Keys Tracker will be in charge of checking if the MTK was already sent to the BE in the last period
|
|
10
|
+
* or schedule to be sent; if not it will be added in an internal cache and sent in the next post.
|
|
11
|
+
*
|
|
12
|
+
* @param log Logger instance
|
|
13
|
+
* @param uniqueKeysCache cache to save unique keys
|
|
14
|
+
* @param filterAdapter filter adapter
|
|
15
|
+
*/
|
|
16
|
+
export function uniqueKeysTrackerFactory(log, uniqueKeysCache, filterAdapter) {
|
|
17
|
+
if (filterAdapter === void 0) { filterAdapter = noopFilterAdapter; }
|
|
18
|
+
var intervalId;
|
|
19
|
+
if (filterAdapter.refreshRate) {
|
|
20
|
+
intervalId = setInterval(filterAdapter.clear, filterAdapter.refreshRate);
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
track: function (key, featureName) {
|
|
24
|
+
if (!filterAdapter.add(key, featureName)) {
|
|
25
|
+
log.debug(LOG_PREFIX_UNIQUE_KEYS_TRACKER + "The feature " + featureName + " and key " + key + " exist in the filter");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
uniqueKeysCache.track(key, featureName);
|
|
29
|
+
},
|
|
30
|
+
stop: function () {
|
|
31
|
+
clearInterval(intervalId);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -13,6 +13,7 @@ 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';
|
|
16
17
|
// SDK Modes
|
|
17
18
|
export var LOCALHOST_MODE = 'localhost';
|
|
18
19
|
export var STANDALONE_MODE = 'standalone';
|
|
@@ -37,6 +38,7 @@ export var CONSUMER_ENUM = 1;
|
|
|
37
38
|
export var CONSUMER_PARTIAL_ENUM = 2;
|
|
38
39
|
export var OPTIMIZED_ENUM = 0;
|
|
39
40
|
export var DEBUG_ENUM = 1;
|
|
41
|
+
export var NONE_ENUM = 2;
|
|
40
42
|
export var SPLITS = 'sp';
|
|
41
43
|
export var IMPRESSIONS = 'im';
|
|
42
44
|
export var IMPRESSIONS_COUNT = 'ic';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
function identityFunction(data) {
|
|
3
|
+
return data;
|
|
4
|
+
}
|
|
5
|
+
function asyncFunction(data) {
|
|
6
|
+
return Promise.resolve(data);
|
|
7
|
+
}
|
|
8
|
+
var IDENTITY_METHODS = [];
|
|
9
|
+
var ASYNC_METHODS = ['rpush', 'hincrby'];
|
|
10
|
+
var PIPELINE_METHODS = ['rpush', 'hincrby'];
|
|
11
|
+
var RedisMock = /** @class */ (function () {
|
|
12
|
+
function RedisMock() {
|
|
13
|
+
var _this = this;
|
|
14
|
+
this.pipelineMethods = { exec: jest.fn(asyncFunction) };
|
|
15
|
+
IDENTITY_METHODS.forEach(function (method) {
|
|
16
|
+
_this[method] = jest.fn(identityFunction);
|
|
17
|
+
});
|
|
18
|
+
ASYNC_METHODS.forEach(function (method) {
|
|
19
|
+
_this[method] = jest.fn(asyncFunction);
|
|
20
|
+
});
|
|
21
|
+
PIPELINE_METHODS.forEach(function (method) {
|
|
22
|
+
_this.pipelineMethods[method] = _this[method];
|
|
23
|
+
});
|
|
24
|
+
this.pipeline = jest.fn(function () { return _this.pipelineMethods; });
|
|
25
|
+
}
|
|
26
|
+
return RedisMock;
|
|
27
|
+
}());
|
|
28
|
+
export { RedisMock };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ERROR_INVALID_CONFIG_PARAM } from '../../logger/constants';
|
|
2
|
-
import { DEBUG, OPTIMIZED } from '../constants';
|
|
2
|
+
import { DEBUG, OPTIMIZED, NONE } from '../constants';
|
|
3
3
|
import { stringToUpperCase } from '../lang';
|
|
4
4
|
export function validImpressionsMode(log, impressionsMode) {
|
|
5
5
|
impressionsMode = stringToUpperCase(impressionsMode);
|
|
6
|
-
if ([DEBUG, OPTIMIZED].indexOf(impressionsMode) > -1)
|
|
6
|
+
if ([DEBUG, OPTIMIZED, NONE].indexOf(impressionsMode) > -1)
|
|
7
7
|
return impressionsMode;
|
|
8
|
-
log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED], OPTIMIZED]);
|
|
8
|
+
log.error(ERROR_INVALID_CONFIG_PARAM, ['impressionsMode', [DEBUG, OPTIMIZED, NONE], OPTIMIZED]);
|
|
9
9
|
return OPTIMIZED;
|
|
10
10
|
}
|
|
@@ -23,8 +23,8 @@ export var base = {
|
|
|
23
23
|
IPAddressesEnabled: undefined
|
|
24
24
|
},
|
|
25
25
|
scheduler: {
|
|
26
|
-
// fetch feature updates each
|
|
27
|
-
featuresRefreshRate:
|
|
26
|
+
// fetch feature updates each 60 sec
|
|
27
|
+
featuresRefreshRate: 60,
|
|
28
28
|
// fetch segments updates each 60 sec
|
|
29
29
|
segmentsRefreshRate: 60,
|
|
30
30
|
// publish telemetry stats each 3600 secs (1 hour)
|
|
@@ -129,8 +129,8 @@ export function settingsValidation(config, validationParams) {
|
|
|
129
129
|
if (storage)
|
|
130
130
|
withDefaults.storage = storage(withDefaults);
|
|
131
131
|
// Validate key and TT (for client-side)
|
|
132
|
+
var maybeKey = withDefaults.core.key;
|
|
132
133
|
if (validationParams.acceptKey) {
|
|
133
|
-
var maybeKey = withDefaults.core.key;
|
|
134
134
|
// Although `key` is required in client-side, it can be omitted in LOCALHOST mode. In that case, the value `localhost_key` is used.
|
|
135
135
|
if (withDefaults.mode === LOCALHOST_MODE && maybeKey === undefined) {
|
|
136
136
|
withDefaults.core.key = 'localhost_key';
|
|
@@ -148,6 +148,12 @@ export function settingsValidation(config, validationParams) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
else {
|
|
152
|
+
// On server-side, key is undefined and used to distinguish from client-side
|
|
153
|
+
if (maybeKey !== undefined)
|
|
154
|
+
log.warn('Provided `key` is ignored in server-side SDK.'); // @ts-ignore
|
|
155
|
+
withDefaults.core.key = undefined;
|
|
156
|
+
}
|
|
151
157
|
// Current ip/hostname information
|
|
152
158
|
// @ts-ignore, modify readonly prop
|
|
153
159
|
withDefaults.runtime = runtime(withDefaults);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Split Javascript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -71,7 +71,6 @@
|
|
|
71
71
|
"ioredis": "^4.28.0",
|
|
72
72
|
"jest": "^27.2.3",
|
|
73
73
|
"jest-localstorage-mock": "^2.4.3",
|
|
74
|
-
"js-yaml": "^3.13.1",
|
|
75
74
|
"lodash": "^4.17.21",
|
|
76
75
|
"node-fetch": "^2.6.7",
|
|
77
76
|
"redis-server": "1.2.2",
|
|
@@ -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) impressionCounts.clear();
|
|
43
|
+
if (impressions.clear) impressions.clear(); // @ts-ignore
|
|
44
|
+
if (impressionCounts && impressionCounts.clear) impressionCounts.clear();
|
|
45
45
|
}
|
|
46
46
|
} else {
|
|
47
47
|
log.info(USER_CONSENT_NOT_UPDATED, [newConsentStatus]);
|
package/src/listeners/browser.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
/* eslint-disable no-undef */
|
|
2
2
|
// @TODO eventually migrate to JS-Browser-SDK package.
|
|
3
3
|
import { ISignalListener } from './types';
|
|
4
|
-
import {
|
|
4
|
+
import { IRecorderCacheSync, IStorageSync } from '../storages/types';
|
|
5
5
|
import { fromImpressionsCollector } from '../sync/submitters/impressionsSubmitter';
|
|
6
6
|
import { fromImpressionCountsCollector } from '../sync/submitters/impressionCountsSubmitter';
|
|
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 } from '../utils/constants';
|
|
10
|
+
import { OPTIMIZED, DEBUG, NONE } 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';
|
|
14
14
|
import { isConsentGranted } from '../consent';
|
|
15
|
-
import { telemetryCacheStatsAdapter } from '../sync/submitters/telemetrySubmitter';
|
|
16
15
|
|
|
17
16
|
const VISIBILITYCHANGE_EVENT = 'visibilitychange';
|
|
18
17
|
const PAGEHIDE_EVENT = 'pagehide';
|
|
@@ -84,26 +83,25 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
84
83
|
*/
|
|
85
84
|
flushData() {
|
|
86
85
|
if (!this.syncManager) return; // In consumer mode there is not sync manager and data to flush
|
|
86
|
+
const { events, telemetry } = this.settings.urls;
|
|
87
87
|
|
|
88
88
|
// Flush impressions & events data if there is user consent
|
|
89
89
|
if (isConsentGranted(this.settings)) {
|
|
90
|
-
const
|
|
90
|
+
const sim = this.settings.sync.impressionsMode;
|
|
91
91
|
const extraMetadata = {
|
|
92
92
|
// sim stands for Sync/Split Impressions Mode
|
|
93
|
-
sim:
|
|
93
|
+
sim: sim === OPTIMIZED ? OPTIMIZED : sim === DEBUG ? DEBUG : NONE
|
|
94
94
|
};
|
|
95
95
|
|
|
96
|
-
this._flushData(
|
|
97
|
-
this._flushData(
|
|
98
|
-
if (this.storage.impressionCounts) this._flushData(
|
|
96
|
+
this._flushData(events + '/testImpressions/beacon', this.storage.impressions, this.serviceApi.postTestImpressionsBulk, this.fromImpressionsCollector, extraMetadata);
|
|
97
|
+
this._flushData(events + '/events/beacon', this.storage.events, this.serviceApi.postEventsBulk);
|
|
98
|
+
if (this.storage.impressionCounts) this._flushData(events + '/testImpressions/count/beacon', this.storage.impressionCounts, this.serviceApi.postTestImpressionsCount, fromImpressionCountsCollector);
|
|
99
|
+
// @ts-ignore
|
|
100
|
+
if (this.storage.uniqueKeys) this._flushData(telemetry + '/v1/keys/cs/beacon', this.storage.uniqueKeys, this.serviceApi.postUniqueKeysBulkCs);
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
// Flush telemetry data
|
|
102
|
-
if (this.storage.telemetry)
|
|
103
|
-
const telemetryUrl = this.settings.urls.telemetry;
|
|
104
|
-
const telemetryCacheAdapter = telemetryCacheStatsAdapter(this.storage.telemetry, this.storage.splits, this.storage.segments);
|
|
105
|
-
this._flushData(telemetryUrl + '/v1/metrics/usage/beacon', telemetryCacheAdapter, this.serviceApi.postMetricsUsage);
|
|
106
|
-
}
|
|
104
|
+
if (this.storage.telemetry) this._flushData(telemetry + '/v1/metrics/usage/beacon', this.storage.telemetry, this.serviceApi.postMetricsUsage);
|
|
107
105
|
}
|
|
108
106
|
|
|
109
107
|
flushDataIfHidden() {
|
|
@@ -111,14 +109,13 @@ export class BrowserSignalListener implements ISignalListener {
|
|
|
111
109
|
if (document.visibilityState === 'hidden') this.flushData(); // On a 'visibilitychange' event, flush data if state is hidden
|
|
112
110
|
}
|
|
113
111
|
|
|
114
|
-
private _flushData<T>(url: string, cache:
|
|
112
|
+
private _flushData<T>(url: string, cache: IRecorderCacheSync<T>, postService: (body: string) => Promise<IResponse>, fromCacheToPayload?: (cacheData: T) => any, extraMetadata?: {}) {
|
|
115
113
|
// if there is data in cache, send it to backend
|
|
116
114
|
if (!cache.isEmpty()) {
|
|
117
115
|
const dataPayload = fromCacheToPayload ? fromCacheToPayload(cache.pop()) : cache.pop();
|
|
118
116
|
if (!this._sendBeacon(url, dataPayload, extraMetadata)) {
|
|
119
117
|
postService(JSON.stringify(dataPayload)).catch(() => { }); // no-op just to catch a possible exception
|
|
120
118
|
}
|
|
121
|
-
cache.clear();
|
|
122
119
|
}
|
|
123
120
|
}
|
|
124
121
|
|
package/src/logger/constants.ts
CHANGED
|
@@ -143,4 +143,5 @@ 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: ';
|
|
146
147
|
export const LOG_PREFIX_CLEANUP = 'cleanup: ';
|
|
@@ -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 } = params;
|
|
12
|
+
const { sdkReadinessManager, syncManager, storage, signalListener, settings, telemetryTracker, uniqueKeysTracker } = params;
|
|
13
13
|
|
|
14
14
|
return objectAssign(
|
|
15
15
|
// Proto-linkage of the readiness Event Emitter
|
|
@@ -40,6 +40,8 @@ export function sdkClientFactory(params: ISdkFactoryContext, isSharedClient?: bo
|
|
|
40
40
|
// Release the API Key if it is the main client
|
|
41
41
|
if (!isSharedClient) releaseApiKey(settings.core.authorizationKey);
|
|
42
42
|
|
|
43
|
+
if (uniqueKeysTracker) uniqueKeysTracker.stop();
|
|
44
|
+
|
|
43
45
|
// Cleanup storage
|
|
44
46
|
return storage.destroy();
|
|
45
47
|
});
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -3,16 +3,17 @@ import { sdkReadinessManagerFactory } from '../readiness/sdkReadinessManager';
|
|
|
3
3
|
import { impressionsTrackerFactory } from '../trackers/impressionsTracker';
|
|
4
4
|
import { eventTrackerFactory } from '../trackers/eventTracker';
|
|
5
5
|
import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
|
|
6
|
-
import { IStorageFactoryParams } from '../storages/types';
|
|
7
6
|
import { SplitIO } from '../types';
|
|
8
|
-
import { getMatching } from '../utils/key';
|
|
9
|
-
import { shouldBeOptimized } from '../trackers/impressionObserver/utils';
|
|
10
7
|
import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
|
|
11
8
|
import { createLoggerAPI } from '../logger/sdkLogger';
|
|
12
9
|
import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
|
|
13
|
-
import { metadataBuilder } from '../storages/metadataBuilder';
|
|
14
10
|
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
|
|
15
11
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
12
|
+
import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
|
|
13
|
+
import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
|
|
14
|
+
import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
|
|
15
|
+
import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
|
|
16
|
+
import { NONE, OPTIMIZED } from '../utils/constants';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Modular SDK factory
|
|
@@ -21,8 +22,9 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
21
22
|
|
|
22
23
|
const { settings, platform, storageFactory, splitApiFactory, extraProps,
|
|
23
24
|
syncManagerFactory, SignalListener, impressionsObserverFactory,
|
|
24
|
-
integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory
|
|
25
|
-
|
|
25
|
+
integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory,
|
|
26
|
+
filterAdapterFactory } = params;
|
|
27
|
+
const { log, sync: { impressionsMode } } = settings;
|
|
26
28
|
|
|
27
29
|
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid API Key, etc.
|
|
28
30
|
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
|
|
@@ -33,45 +35,41 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
33
35
|
const sdkReadinessManager = sdkReadinessManagerFactory(log, platform.EventEmitter, settings.startup.readyTimeout);
|
|
34
36
|
const readiness = sdkReadinessManager.readinessManager;
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
impressionsQueueSize: settings.scheduler.impressionsQueueSize,
|
|
39
|
-
eventsQueueSize: settings.scheduler.eventsQueueSize,
|
|
40
|
-
optimize: shouldBeOptimized(settings),
|
|
41
|
-
|
|
42
|
-
// ATM, only used by InLocalStorage
|
|
43
|
-
matchingKey: getMatching(settings.core.key),
|
|
44
|
-
splitFiltersValidation: settings.sync.__splitFiltersValidation,
|
|
45
|
-
|
|
46
|
-
// ATM, only used by PluggableStorage
|
|
47
|
-
mode: settings.mode,
|
|
48
|
-
|
|
49
|
-
// Callback used to emit SDK_READY in consumer mode, where `syncManagerFactory` is undefined,
|
|
50
|
-
// or partial consumer mode, where it only has submitters, and therefore it doesn't emit readiness events.
|
|
38
|
+
const storage = storageFactory({
|
|
39
|
+
settings,
|
|
51
40
|
onReadyCb: (error) => {
|
|
52
41
|
if (error) return; // Don't emit SDK_READY if storage failed to connect. Error message is logged by wrapperAdapter
|
|
53
42
|
readiness.splits.emit(SDK_SPLITS_ARRIVED);
|
|
54
43
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
55
44
|
},
|
|
56
|
-
|
|
57
|
-
log
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const storage = storageFactory(storageFactoryParams);
|
|
45
|
+
});
|
|
61
46
|
// @TODO add support for dataloader: `if (params.dataLoader) params.dataLoader(storage);`
|
|
62
47
|
|
|
63
48
|
const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
|
|
64
49
|
const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
|
|
65
50
|
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
51
|
+
const observer = impressionsObserverFactory();
|
|
52
|
+
const uniqueKeysTracker = impressionsMode === NONE ? uniqueKeysTrackerFactory(log, storage.uniqueKeys!, filterAdapterFactory && filterAdapterFactory()) : undefined;
|
|
53
|
+
|
|
54
|
+
let strategy;
|
|
55
|
+
switch (impressionsMode) {
|
|
56
|
+
case OPTIMIZED:
|
|
57
|
+
strategy = strategyOptimizedFactory(observer, storage.impressionCounts!);
|
|
58
|
+
break;
|
|
59
|
+
case NONE:
|
|
60
|
+
strategy = strategyNoneFactory(storage.impressionCounts!, uniqueKeysTracker!);
|
|
61
|
+
break;
|
|
62
|
+
default:
|
|
63
|
+
strategy = strategyDebugFactory(observer);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, strategy, integrationsManager, storage.telemetry);
|
|
69
67
|
const eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
|
|
70
68
|
|
|
71
69
|
// splitApi is used by SyncManager and Browser signal listener
|
|
72
70
|
const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
|
|
73
71
|
|
|
74
|
-
const ctx: ISdkFactoryContext = { splitApi, eventTracker, impressionsTracker, telemetryTracker, sdkReadinessManager, readiness, settings, storage, platform };
|
|
72
|
+
const ctx: ISdkFactoryContext = { splitApi, eventTracker, impressionsTracker, telemetryTracker, uniqueKeysTracker, sdkReadinessManager, readiness, settings, storage, platform };
|
|
75
73
|
|
|
76
74
|
const syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync);
|
|
77
75
|
ctx.syncManager = syncManager;
|
|
@@ -100,7 +98,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
|
|
|
100
98
|
},
|
|
101
99
|
|
|
102
100
|
// Logger wrapper API
|
|
103
|
-
Logger: createLoggerAPI(
|
|
101
|
+
Logger: createLoggerAPI(log),
|
|
104
102
|
|
|
105
103
|
settings,
|
|
106
104
|
}, extraProps && extraProps(ctx));
|
package/src/sdkFactory/types.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types';
|
|
|
6
6
|
import { IStorageAsync, IStorageSync, ISplitsCacheSync, ISplitsCacheAsync, IStorageFactoryParams } from '../storages/types';
|
|
7
7
|
import { ISyncManager } from '../sync/types';
|
|
8
8
|
import { IImpressionObserver } from '../trackers/impressionObserver/types';
|
|
9
|
-
import { IImpressionsTracker, IEventTracker, ITelemetryTracker } from '../trackers/types';
|
|
9
|
+
import { IImpressionsTracker, IEventTracker, ITelemetryTracker, IFilterAdapter, IUniqueKeysTracker } from '../trackers/types';
|
|
10
10
|
import { SplitIO, ISettings, IEventEmitter } from '../types';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -44,6 +44,7 @@ export interface ISdkFactoryContext {
|
|
|
44
44
|
eventTracker: IEventTracker,
|
|
45
45
|
telemetryTracker: ITelemetryTracker,
|
|
46
46
|
storage: IStorageSync | IStorageAsync,
|
|
47
|
+
uniqueKeysTracker?: IUniqueKeysTracker,
|
|
47
48
|
signalListener?: ISignalListener
|
|
48
49
|
splitApi?: ISplitApi
|
|
49
50
|
syncManager?: ISyncManager,
|
|
@@ -96,6 +97,11 @@ export interface ISdkFactoryParams {
|
|
|
96
97
|
// It Allows to distinguish SDK clients with the client-side API (`ICsSDK`) or server-side API (`ISDK` or `IAsyncSDK`).
|
|
97
98
|
sdkClientMethodFactory: (params: ISdkFactoryContext) => ({ (): SplitIO.ICsClient; (key: SplitIO.SplitKey, trafficType?: string | undefined): SplitIO.ICsClient; } | (() => SplitIO.IClient) | (() => SplitIO.IAsyncClient))
|
|
98
99
|
|
|
100
|
+
// Impression observer factory.
|
|
101
|
+
impressionsObserverFactory: () => IImpressionObserver
|
|
102
|
+
|
|
103
|
+
filterAdapterFactory?: () => IFilterAdapter
|
|
104
|
+
|
|
99
105
|
// Optional signal listener constructor. Used to handle special app states, like shutdown, app paused or resumed.
|
|
100
106
|
// Pass only if `syncManager` (used by Node listener) and `splitApi` (used by Browser listener) are passed.
|
|
101
107
|
SignalListener?: new (
|
|
@@ -107,9 +113,6 @@ export interface ISdkFactoryParams {
|
|
|
107
113
|
// @TODO review impressionListener and integrations interfaces. What about handling impressionListener as an integration ?
|
|
108
114
|
integrationsManagerFactory?: (params: IIntegrationFactoryParams) => IIntegrationManager | undefined,
|
|
109
115
|
|
|
110
|
-
// Impression observer factory. If provided, will be used for impressions dedupe
|
|
111
|
-
impressionsObserverFactory?: () => IImpressionObserver
|
|
112
|
-
|
|
113
116
|
// Optional function to assign additional properties to the factory instance
|
|
114
117
|
extraProps?: (params: ISdkFactoryContext) => object
|
|
115
118
|
}
|
package/src/services/splitApi.ts
CHANGED
|
@@ -107,6 +107,28 @@ export function splitApiFactory(
|
|
|
107
107
|
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT));
|
|
108
108
|
},
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Post unique keys for client side.
|
|
112
|
+
*
|
|
113
|
+
* @param body unique keys payload
|
|
114
|
+
* @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
115
|
+
*/
|
|
116
|
+
postUniqueKeysBulkCs(body: string, headers?: Record<string, string>) {
|
|
117
|
+
const url = `${urls.telemetry}/v1/keys/cs`;
|
|
118
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Post unique keys for server side.
|
|
123
|
+
*
|
|
124
|
+
* @param body unique keys payload
|
|
125
|
+
* @param headers Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
126
|
+
*/
|
|
127
|
+
postUniqueKeysBulkSs(body: string, headers?: Record<string, string>) {
|
|
128
|
+
const url = `${urls.telemetry}/v1/keys/ss`;
|
|
129
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
|
|
130
|
+
},
|
|
131
|
+
|
|
110
132
|
postMetricsConfig(body: string, headers?: Record<string, string>) {
|
|
111
133
|
const url = `${urls.telemetry}/v1/metrics/config`;
|
|
112
134
|
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
|
package/src/services/types.ts
CHANGED
|
@@ -43,6 +43,10 @@ export type IFetchMySegments = (userMatchingKey: string, noCache?: boolean) => P
|
|
|
43
43
|
|
|
44
44
|
export type IPostEventsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
45
45
|
|
|
46
|
+
export type IPostUniqueKeysBulkCs = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
47
|
+
|
|
48
|
+
export type IPostUniqueKeysBulkSs = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
49
|
+
|
|
46
50
|
export type IPostTestImpressionsBulk = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
47
51
|
|
|
48
52
|
export type IPostTestImpressionsCount = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
@@ -59,6 +63,8 @@ export interface ISplitApi {
|
|
|
59
63
|
fetchSegmentChanges: IFetchSegmentChanges
|
|
60
64
|
fetchMySegments: IFetchMySegments
|
|
61
65
|
postEventsBulk: IPostEventsBulk
|
|
66
|
+
postUniqueKeysBulkCs: IPostUniqueKeysBulkCs
|
|
67
|
+
postUniqueKeysBulkSs: IPostUniqueKeysBulkSs
|
|
62
68
|
postTestImpressionsBulk: IPostTestImpressionsBulk
|
|
63
69
|
postTestImpressionsCount: IPostTestImpressionsCount
|
|
64
70
|
postMetricsConfig: IPostMetricsConfig
|
|
@@ -41,7 +41,7 @@ export abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
|
|
|
41
41
|
* @param {string} name
|
|
42
42
|
* @param {string} defaultTreatment
|
|
43
43
|
* @param {number} changeNumber
|
|
44
|
-
* @returns {Promise} a promise that is resolved once the split kill operation is performed. The fulfillment value is a boolean: `true` if the
|
|
44
|
+
* @returns {Promise} a promise that is resolved once the split kill operation is performed. The fulfillment value is a boolean: `true` if the operation successed updating the split or `false` if no split is updated,
|
|
45
45
|
* for instance, if the `changeNumber` is old, or if the split is not found (e.g., `/splitchanges` hasn't been fetched yet), or if the storage fails to apply the update.
|
|
46
46
|
* The promise will never be rejected.
|
|
47
47
|
*/
|