@splitsoftware/splitio-commons 1.6.2-rc.5 → 1.6.2-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +3 -0
- package/cjs/evaluator/index.js +10 -11
- package/cjs/integrations/ga/GaToSplit.js +8 -5
- package/cjs/sdkClient/sdkClient.js +3 -1
- package/cjs/sdkFactory/index.js +2 -2
- package/cjs/sdkManager/index.js +3 -11
- package/cjs/storages/AbstractSplitsCacheAsync.js +7 -9
- package/cjs/storages/AbstractSplitsCacheSync.js +7 -9
- package/cjs/storages/dataLoader.js +1 -1
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +5 -6
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +7 -10
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +10 -6
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +15 -9
- package/cjs/storages/inRedis/index.js +4 -3
- package/cjs/storages/inRedis/uniqueKeysCacheInRedis.js +11 -7
- package/cjs/storages/pluggable/SplitsCachePluggable.js +14 -9
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/cjs/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/cjs/trackers/strategy/strategyOptimized.js +2 -1
- package/cjs/trackers/telemetryTracker.js +6 -0
- package/cjs/trackers/uniqueKeysTracker.js +8 -1
- package/esm/evaluator/index.js +10 -11
- package/esm/integrations/ga/GaToSplit.js +8 -5
- package/esm/sdkClient/sdkClient.js +3 -1
- package/esm/sdkFactory/index.js +2 -2
- package/esm/sdkManager/index.js +3 -11
- package/esm/storages/AbstractSplitsCacheAsync.js +7 -9
- package/esm/storages/AbstractSplitsCacheSync.js +7 -9
- package/esm/storages/dataLoader.js +1 -1
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +5 -6
- package/esm/storages/inMemory/SplitsCacheInMemory.js +7 -10
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +10 -6
- package/esm/storages/inRedis/SplitsCacheInRedis.js +15 -9
- package/esm/storages/inRedis/index.js +4 -3
- package/esm/storages/inRedis/uniqueKeysCacheInRedis.js +11 -7
- package/esm/storages/pluggable/SplitsCachePluggable.js +14 -9
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +2 -3
- package/esm/sync/polling/updaters/splitChangesUpdater.js +1 -1
- package/esm/trackers/strategy/strategyOptimized.js +2 -1
- package/esm/trackers/telemetryTracker.js +6 -0
- package/esm/trackers/uniqueKeysTracker.js +8 -1
- package/package.json +1 -1
- package/src/evaluator/index.ts +8 -9
- package/src/integrations/ga/GaToSplit.ts +9 -5
- package/src/integrations/types.ts +2 -1
- package/src/sdkClient/sdkClient.ts +3 -1
- package/src/sdkFactory/index.ts +2 -2
- package/src/sdkManager/index.ts +3 -12
- package/src/storages/AbstractSplitsCacheAsync.ts +12 -14
- package/src/storages/AbstractSplitsCacheSync.ts +14 -16
- package/src/storages/dataLoader.ts +1 -1
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +8 -10
- package/src/storages/inMemory/SplitsCacheInMemory.ts +10 -14
- package/src/storages/inRedis/ImpressionCountsCacheInRedis.ts +10 -6
- package/src/storages/inRedis/SplitsCacheInRedis.ts +21 -17
- package/src/storages/inRedis/index.ts +5 -4
- package/src/storages/inRedis/uniqueKeysCacheInRedis.ts +11 -8
- package/src/storages/pluggable/SplitsCachePluggable.ts +20 -17
- package/src/storages/types.ts +13 -13
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +5 -6
- package/src/sync/polling/updaters/splitChangesUpdater.ts +2 -2
- package/src/trackers/strategy/strategyOptimized.ts +1 -1
- package/src/trackers/telemetryTracker.ts +7 -2
- package/src/trackers/types.ts +6 -0
- package/src/trackers/uniqueKeysTracker.ts +13 -2
- package/types/integrations/types.d.ts +2 -1
- package/types/storages/AbstractSplitsCacheAsync.d.ts +6 -5
- package/types/storages/AbstractSplitsCacheSync.d.ts +5 -5
- package/types/storages/inLocalStorage/SplitsCacheInLocal.d.ts +3 -3
- package/types/storages/inMemory/SplitsCacheInMemory.d.ts +3 -2
- package/types/storages/inRedis/ImpressionCountsCacheInRedis.d.ts +5 -4
- package/types/storages/inRedis/SplitsCacheInRedis.d.ts +6 -5
- package/types/storages/inRedis/uniqueKeysCacheInRedis.d.ts +5 -4
- package/types/storages/pluggable/SplitsCachePluggable.d.ts +6 -5
- package/types/storages/types.d.ts +13 -13
- package/types/sync/polling/updaters/splitChangesUpdater.d.ts +1 -1
- package/types/trackers/types.d.ts +6 -0
- package/types/trackers/uniqueKeysTracker.d.ts +1 -1
|
@@ -11,20 +11,22 @@ export class UniqueKeysCacheInRedis extends UniqueKeysCacheInMemory implements I
|
|
|
11
11
|
private readonly log: ILogger;
|
|
12
12
|
private readonly key: string;
|
|
13
13
|
private readonly redis: Redis;
|
|
14
|
-
private
|
|
14
|
+
private readonly refreshRate: number;
|
|
15
|
+
private intervalId: any;
|
|
15
16
|
|
|
16
|
-
constructor(log: ILogger, key: string, redis: Redis, uniqueKeysQueueSize: number = DEFAULT_CACHE_SIZE) {
|
|
17
|
+
constructor(log: ILogger, key: string, redis: Redis, uniqueKeysQueueSize: number = DEFAULT_CACHE_SIZE, refreshRate: number = REFRESH_RATE) {
|
|
17
18
|
super(uniqueKeysQueueSize);
|
|
18
19
|
this.log = log;
|
|
19
20
|
this.key = key;
|
|
20
21
|
this.redis = redis;
|
|
22
|
+
this.refreshRate = refreshRate;
|
|
21
23
|
this.onFullQueue = () => {this.postUniqueKeysInRedis();};
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
postUniqueKeysInRedis() {
|
|
25
|
-
const pipeline = this.redis.pipeline();
|
|
26
|
-
|
|
27
27
|
const featureNames = Object.keys(this.uniqueKeysTracker);
|
|
28
|
+
if (!featureNames) return Promise.resolve(false);
|
|
29
|
+
const pipeline = this.redis.pipeline();
|
|
28
30
|
for (let i = 0; i < featureNames.length; i++) {
|
|
29
31
|
const featureName = featureNames[i];
|
|
30
32
|
const featureKeys = setToArray(this.uniqueKeysTracker[featureName]);
|
|
@@ -45,17 +47,18 @@ export class UniqueKeysCacheInRedis extends UniqueKeysCacheInMemory implements I
|
|
|
45
47
|
})
|
|
46
48
|
.catch(err => {
|
|
47
49
|
this.log.error(`${LOG_PREFIX}Error in uniqueKeys pipeline: ${err}.`);
|
|
48
|
-
return false;
|
|
50
|
+
return Promise.resolve(false);
|
|
49
51
|
});
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
|
|
53
|
-
start(
|
|
54
|
-
this.
|
|
55
|
+
start() {
|
|
56
|
+
this.intervalId = setInterval(this.postUniqueKeysInRedis.bind(this), this.refreshRate);
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
stop() {
|
|
58
|
-
clearInterval(this.
|
|
60
|
+
clearInterval(this.intervalId);
|
|
61
|
+
return this.postUniqueKeysInRedis();
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
}
|
|
@@ -49,22 +49,22 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
49
49
|
* The returned promise is resolved when the operation success
|
|
50
50
|
* or rejected if it fails (e.g., wrapper operation fails)
|
|
51
51
|
*/
|
|
52
|
-
addSplit(name: string, split:
|
|
52
|
+
addSplit(name: string, split: ISplit): Promise<boolean> {
|
|
53
53
|
const splitKey = this.keys.buildSplitKey(name);
|
|
54
54
|
return this.wrapper.get(splitKey).then(splitFromStorage => {
|
|
55
55
|
|
|
56
56
|
// handling parsing error
|
|
57
|
-
let parsedPreviousSplit,
|
|
57
|
+
let parsedPreviousSplit, stringifiedNewSplit;
|
|
58
58
|
try {
|
|
59
59
|
parsedPreviousSplit = splitFromStorage ? JSON.parse(splitFromStorage) : undefined;
|
|
60
|
-
|
|
60
|
+
stringifiedNewSplit = JSON.stringify(split);
|
|
61
61
|
} catch (e) {
|
|
62
62
|
throw new Error('Error parsing split definition: ' + e);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
return Promise.all([
|
|
66
|
-
this.wrapper.set(splitKey,
|
|
67
|
-
this._incrementCounts(
|
|
66
|
+
this.wrapper.set(splitKey, stringifiedNewSplit),
|
|
67
|
+
this._incrementCounts(split),
|
|
68
68
|
// If it's an update, we decrement the traffic type and segment count of the existing split,
|
|
69
69
|
parsedPreviousSplit && this._decrementCounts(parsedPreviousSplit)
|
|
70
70
|
]);
|
|
@@ -76,7 +76,7 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
76
76
|
* The returned promise is resolved when the operation success
|
|
77
77
|
* or rejected if it fails (e.g., wrapper operation fails)
|
|
78
78
|
*/
|
|
79
|
-
addSplits(entries: [string,
|
|
79
|
+
addSplits(entries: [string, ISplit][]): Promise<boolean[]> {
|
|
80
80
|
return Promise.all(entries.map(keyValuePair => this.addSplit(keyValuePair[0], keyValuePair[1])));
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -88,8 +88,7 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
88
88
|
removeSplit(name: string) {
|
|
89
89
|
return this.getSplit(name).then((split) => {
|
|
90
90
|
if (split) {
|
|
91
|
-
|
|
92
|
-
this._decrementCounts(parsedSplit);
|
|
91
|
+
this._decrementCounts(split);
|
|
93
92
|
}
|
|
94
93
|
return this.wrapper.del(this.keys.buildSplitKey(name));
|
|
95
94
|
});
|
|
@@ -109,8 +108,9 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
109
108
|
* The returned promise is resolved with the split definition or null if it's not defined,
|
|
110
109
|
* or rejected if wrapper operation fails.
|
|
111
110
|
*/
|
|
112
|
-
getSplit(name: string): Promise<
|
|
113
|
-
return this.wrapper.get(this.keys.buildSplitKey(name))
|
|
111
|
+
getSplit(name: string): Promise<ISplit | null> {
|
|
112
|
+
return this.wrapper.get(this.keys.buildSplitKey(name))
|
|
113
|
+
.then(maybeSplit => maybeSplit && JSON.parse(maybeSplit));
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/**
|
|
@@ -118,13 +118,14 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
118
118
|
* The returned promise is resolved with a map of split names to their split definition or null if it's not defined,
|
|
119
119
|
* or rejected if wrapper operation fails.
|
|
120
120
|
*/
|
|
121
|
-
getSplits(names: string[]): Promise<Record<string,
|
|
121
|
+
getSplits(names: string[]): Promise<Record<string, ISplit | null>> {
|
|
122
122
|
const keys = names.map(name => this.keys.buildSplitKey(name));
|
|
123
123
|
|
|
124
124
|
return this.wrapper.getMany(keys).then(splitDefinitions => {
|
|
125
|
-
const splits: Record<string,
|
|
125
|
+
const splits: Record<string, ISplit | null> = {};
|
|
126
126
|
names.forEach((name, idx) => {
|
|
127
|
-
|
|
127
|
+
const split = splitDefinitions[idx];
|
|
128
|
+
splits[name] = split && JSON.parse(split);
|
|
128
129
|
});
|
|
129
130
|
return Promise.resolve(splits);
|
|
130
131
|
});
|
|
@@ -135,10 +136,12 @@ export class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
135
136
|
* The returned promise is resolved with the list of split definitions,
|
|
136
137
|
* or rejected if wrapper operation fails.
|
|
137
138
|
*/
|
|
138
|
-
getAll(): Promise<
|
|
139
|
-
return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix())
|
|
140
|
-
(listOfKeys) =>
|
|
141
|
-
|
|
139
|
+
getAll(): Promise<ISplit[]> {
|
|
140
|
+
return this.wrapper.getKeysByPrefix(this.keys.buildSplitKeyPrefix())
|
|
141
|
+
.then((listOfKeys) => this.wrapper.getMany(listOfKeys))
|
|
142
|
+
.then((splitDefinitions) => splitDefinitions.map((splitDefinition) => {
|
|
143
|
+
return JSON.parse(splitDefinition as string);
|
|
144
|
+
}));
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
/**
|
package/src/storages/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types';
|
|
1
|
+
import { MaybeThenable, IMetadata, ISplitFiltersValidation, ISplit } from '../dtos/types';
|
|
2
2
|
import { ILogger } from '../logger/types';
|
|
3
3
|
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs } from '../sync/submitters/types';
|
|
4
4
|
import { SplitIO, ImpressionDTO, SDKMode } from '../types';
|
|
@@ -191,14 +191,14 @@ export interface IPluggableStorageWrapper {
|
|
|
191
191
|
/** Splits cache */
|
|
192
192
|
|
|
193
193
|
export interface ISplitsCacheBase {
|
|
194
|
-
addSplits(entries: [string,
|
|
194
|
+
addSplits(entries: [string, ISplit][]): MaybeThenable<boolean[] | void>,
|
|
195
195
|
removeSplits(names: string[]): MaybeThenable<boolean[] | void>,
|
|
196
|
-
getSplit(name: string): MaybeThenable<
|
|
197
|
-
getSplits(names: string[]): MaybeThenable<Record<string,
|
|
196
|
+
getSplit(name: string): MaybeThenable<ISplit | null>,
|
|
197
|
+
getSplits(names: string[]): MaybeThenable<Record<string, ISplit | null>>, // `fetchMany` in spec
|
|
198
198
|
setChangeNumber(changeNumber: number): MaybeThenable<boolean | void>,
|
|
199
199
|
// should never reject or throw an exception. Instead return -1 by default, assuming no splits are present in the storage.
|
|
200
200
|
getChangeNumber(): MaybeThenable<number>,
|
|
201
|
-
getAll(): MaybeThenable<
|
|
201
|
+
getAll(): MaybeThenable<ISplit[]>,
|
|
202
202
|
getSplitNames(): MaybeThenable<string[]>,
|
|
203
203
|
// should never reject or throw an exception. Instead return true by default, asssuming the TT might exist.
|
|
204
204
|
trafficTypeExists(trafficType: string): MaybeThenable<boolean>,
|
|
@@ -211,13 +211,13 @@ export interface ISplitsCacheBase {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
214
|
-
addSplits(entries: [string,
|
|
214
|
+
addSplits(entries: [string, ISplit][]): boolean[],
|
|
215
215
|
removeSplits(names: string[]): boolean[],
|
|
216
|
-
getSplit(name: string):
|
|
217
|
-
getSplits(names: string[]): Record<string,
|
|
216
|
+
getSplit(name: string): ISplit | null,
|
|
217
|
+
getSplits(names: string[]): Record<string, ISplit | null>,
|
|
218
218
|
setChangeNumber(changeNumber: number): boolean,
|
|
219
219
|
getChangeNumber(): number,
|
|
220
|
-
getAll():
|
|
220
|
+
getAll(): ISplit[],
|
|
221
221
|
getSplitNames(): string[],
|
|
222
222
|
trafficTypeExists(trafficType: string): boolean,
|
|
223
223
|
usesSegments(): boolean,
|
|
@@ -227,13 +227,13 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
230
|
-
addSplits(entries: [string,
|
|
230
|
+
addSplits(entries: [string, ISplit][]): Promise<boolean[] | void>,
|
|
231
231
|
removeSplits(names: string[]): Promise<boolean[] | void>,
|
|
232
|
-
getSplit(name: string): Promise<
|
|
233
|
-
getSplits(names: string[]): Promise<Record<string,
|
|
232
|
+
getSplit(name: string): Promise<ISplit | null>,
|
|
233
|
+
getSplits(names: string[]): Promise<Record<string, ISplit | null>>,
|
|
234
234
|
setChangeNumber(changeNumber: number): Promise<boolean | void>,
|
|
235
235
|
getChangeNumber(): Promise<number>,
|
|
236
|
-
getAll(): Promise<
|
|
236
|
+
getAll(): Promise<ISplit[]>,
|
|
237
237
|
getSplitNames(): Promise<string[]>,
|
|
238
238
|
trafficTypeExists(trafficType: string): Promise<boolean>,
|
|
239
239
|
usesSegments(): Promise<boolean>,
|
|
@@ -2,7 +2,7 @@ import { forOwn } from '../../../utils/lang';
|
|
|
2
2
|
import { IReadinessManager } from '../../../readiness/types';
|
|
3
3
|
import { ISplitsCacheSync } from '../../../storages/types';
|
|
4
4
|
import { ISplitsParser } from '../splitsParser/types';
|
|
5
|
-
import { ISplitPartial } from '../../../dtos/types';
|
|
5
|
+
import { ISplit, ISplitPartial } from '../../../dtos/types';
|
|
6
6
|
import { syncTaskFactory } from '../../syncTask';
|
|
7
7
|
import { ISyncTask } from '../../types';
|
|
8
8
|
import { ISettings } from '../../../types';
|
|
@@ -24,7 +24,7 @@ export function fromObjectUpdaterFactory(
|
|
|
24
24
|
let startingUp = true;
|
|
25
25
|
|
|
26
26
|
return function objectUpdater() {
|
|
27
|
-
const splits: [string,
|
|
27
|
+
const splits: [string, ISplit][] = [];
|
|
28
28
|
let loadError = null;
|
|
29
29
|
let splitsMock: false | Record<string, ISplitPartial> = {};
|
|
30
30
|
try {
|
|
@@ -38,9 +38,8 @@ export function fromObjectUpdaterFactory(
|
|
|
38
38
|
log.debug(SYNC_OFFLINE_DATA, [JSON.stringify(splitsMock)]);
|
|
39
39
|
|
|
40
40
|
forOwn(splitsMock, function (val, name) {
|
|
41
|
-
splits.push([
|
|
42
|
-
name,
|
|
43
|
-
JSON.stringify({
|
|
41
|
+
splits.push([ // @ts-ignore Split changeNumber and seed is undefined in localhost mode
|
|
42
|
+
name, {
|
|
44
43
|
name,
|
|
45
44
|
status: 'ACTIVE',
|
|
46
45
|
killed: false,
|
|
@@ -49,7 +48,7 @@ export function fromObjectUpdaterFactory(
|
|
|
49
48
|
conditions: val.conditions || [],
|
|
50
49
|
configurations: val.configurations,
|
|
51
50
|
trafficTypeName: val.trafficTypeName
|
|
52
|
-
}
|
|
51
|
+
}
|
|
53
52
|
]);
|
|
54
53
|
});
|
|
55
54
|
|
|
@@ -40,7 +40,7 @@ export function parseSegments({ conditions }: ISplit): ISet<string> {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
interface ISplitMutations {
|
|
43
|
-
added: [string,
|
|
43
|
+
added: [string, ISplit][],
|
|
44
44
|
removed: string[],
|
|
45
45
|
segments: string[]
|
|
46
46
|
}
|
|
@@ -54,7 +54,7 @@ export function computeSplitsMutation(entries: ISplit[]): ISplitMutations {
|
|
|
54
54
|
const segments = new _Set<string>();
|
|
55
55
|
const computed = entries.reduce((accum, split) => {
|
|
56
56
|
if (split.status === 'ACTIVE') {
|
|
57
|
-
accum.added.push([split.name,
|
|
57
|
+
accum.added.push([split.name, split]);
|
|
58
58
|
|
|
59
59
|
parseSegments(split).forEach((segmentName: string) => {
|
|
60
60
|
segments.add(segmentName);
|
|
@@ -25,7 +25,7 @@ export function strategyOptimizedFactory(
|
|
|
25
25
|
const now = Date.now();
|
|
26
26
|
|
|
27
27
|
// Increments impression counter per featureName
|
|
28
|
-
impressionsCounter.track(impression.feature, now, 1);
|
|
28
|
+
if (impression.pt) impressionsCounter.track(impression.feature, now, 1);
|
|
29
29
|
|
|
30
30
|
// Checks if the impression should be added in queue to be sent
|
|
31
31
|
if (!impression.pt || impression.pt < truncateTimeFrame(now)) {
|
|
@@ -48,6 +48,10 @@ export function telemetryTrackerFactory(
|
|
|
48
48
|
});
|
|
49
49
|
if (e === TOKEN_REFRESH) (telemetryCache as ITelemetryCacheSync).recordTokenRefreshes();
|
|
50
50
|
}
|
|
51
|
+
},
|
|
52
|
+
addTag(tag: string) {
|
|
53
|
+
// @ts-ignore
|
|
54
|
+
if (telemetryCache.addTag) telemetryCache.addTag(tag);
|
|
51
55
|
}
|
|
52
56
|
};
|
|
53
57
|
|
|
@@ -56,8 +60,9 @@ export function telemetryTrackerFactory(
|
|
|
56
60
|
return {
|
|
57
61
|
trackEval: noopTrack,
|
|
58
62
|
trackHttp: noopTrack,
|
|
59
|
-
sessionLength
|
|
60
|
-
streamingEvent
|
|
63
|
+
sessionLength() { },
|
|
64
|
+
streamingEvent() { },
|
|
65
|
+
addTag() { }
|
|
61
66
|
};
|
|
62
67
|
}
|
|
63
68
|
}
|
package/src/trackers/types.ts
CHANGED
|
@@ -41,12 +41,17 @@ export interface ITelemetryTracker {
|
|
|
41
41
|
* Records streaming event
|
|
42
42
|
*/
|
|
43
43
|
streamingEvent(e: StreamingEventType | AUTH_REJECTION, d?: number): void
|
|
44
|
+
/**
|
|
45
|
+
* Records tag
|
|
46
|
+
*/
|
|
47
|
+
addTag(tag: string): void
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
export interface IFilterAdapter {
|
|
47
51
|
add(key: string, featureName: string): boolean;
|
|
48
52
|
contains(key: string, featureName: string): boolean;
|
|
49
53
|
clear(): void;
|
|
54
|
+
refreshRate?: number;
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
export interface IImpressionSenderAdapter {
|
|
@@ -56,6 +61,7 @@ export interface IImpressionSenderAdapter {
|
|
|
56
61
|
|
|
57
62
|
/** Unique keys tracker */
|
|
58
63
|
export interface IUniqueKeysTracker {
|
|
64
|
+
stop(): void;
|
|
59
65
|
track(key: string, featureName: string): void;
|
|
60
66
|
}
|
|
61
67
|
|
|
@@ -15,23 +15,34 @@ const noopFilterAdapter = {
|
|
|
15
15
|
* or schedule to be sent; if not it will be added in an internal cache and sent in the next post.
|
|
16
16
|
*
|
|
17
17
|
* @param log Logger instance
|
|
18
|
-
* @param filterAdapter filter adapter
|
|
19
18
|
* @param uniqueKeysCache cache to save unique keys
|
|
19
|
+
* @param filterAdapter filter adapter
|
|
20
20
|
*/
|
|
21
21
|
export function uniqueKeysTrackerFactory(
|
|
22
22
|
log: ILogger,
|
|
23
23
|
uniqueKeysCache: IUniqueKeysCacheBase,
|
|
24
24
|
filterAdapter: IFilterAdapter = noopFilterAdapter,
|
|
25
25
|
): IUniqueKeysTracker {
|
|
26
|
-
|
|
26
|
+
let intervalId: any;
|
|
27
|
+
|
|
28
|
+
if (filterAdapter.refreshRate) {
|
|
29
|
+
intervalId = setInterval(filterAdapter.clear, filterAdapter.refreshRate);
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
return {
|
|
33
|
+
|
|
28
34
|
track(key: string, featureName: string): void {
|
|
29
35
|
if (!filterAdapter.add(key, featureName)) {
|
|
30
36
|
log.debug(`${LOG_PREFIX_UNIQUE_KEYS_TRACKER}The feature ${featureName} and key ${key} exist in the filter`);
|
|
31
37
|
return;
|
|
32
38
|
}
|
|
33
39
|
uniqueKeysCache.track(key, featureName);
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
stop(): void {
|
|
43
|
+
clearInterval(intervalId);
|
|
34
44
|
}
|
|
45
|
+
|
|
35
46
|
};
|
|
36
47
|
|
|
37
48
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IEventsCacheBase } from '../storages/types';
|
|
2
|
-
import { IEventsHandler, IImpressionsHandler } from '../trackers/types';
|
|
2
|
+
import { IEventsHandler, IImpressionsHandler, ITelemetryTracker } from '../trackers/types';
|
|
3
3
|
import { ISettings, SplitIO } from '../types';
|
|
4
4
|
export interface IIntegration {
|
|
5
5
|
queue(data: SplitIO.IntegrationData): void;
|
|
@@ -10,6 +10,7 @@ export interface IIntegrationFactoryParams {
|
|
|
10
10
|
events: IEventsCacheBase;
|
|
11
11
|
};
|
|
12
12
|
settings: ISettings;
|
|
13
|
+
telemetryTracker: ITelemetryTracker;
|
|
13
14
|
}
|
|
14
15
|
export declare type IntegrationFactory = {
|
|
15
16
|
readonly type: string;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { ISplitsCacheAsync } from './types';
|
|
2
|
+
import { ISplit } from '../dtos/types';
|
|
2
3
|
/**
|
|
3
4
|
* This class provides a skeletal implementation of the ISplitsCacheAsync interface
|
|
4
5
|
* to minimize the effort required to implement this interface.
|
|
5
6
|
*/
|
|
6
7
|
export declare abstract class AbstractSplitsCacheAsync implements ISplitsCacheAsync {
|
|
7
|
-
abstract addSplit(name: string, split:
|
|
8
|
-
abstract addSplits(entries: [string,
|
|
8
|
+
abstract addSplit(name: string, split: ISplit): Promise<boolean>;
|
|
9
|
+
abstract addSplits(entries: [string, ISplit][]): Promise<boolean[] | void>;
|
|
9
10
|
abstract removeSplits(names: string[]): Promise<boolean[] | void>;
|
|
10
|
-
abstract getSplit(name: string): Promise<
|
|
11
|
-
abstract getSplits(names: string[]): Promise<Record<string,
|
|
11
|
+
abstract getSplit(name: string): Promise<ISplit | null>;
|
|
12
|
+
abstract getSplits(names: string[]): Promise<Record<string, ISplit | null>>;
|
|
12
13
|
abstract setChangeNumber(changeNumber: number): Promise<boolean | void>;
|
|
13
14
|
abstract getChangeNumber(): Promise<number>;
|
|
14
|
-
abstract getAll(): Promise<
|
|
15
|
+
abstract getAll(): Promise<ISplit[]>;
|
|
15
16
|
abstract getSplitNames(): Promise<string[]>;
|
|
16
17
|
abstract trafficTypeExists(trafficType: string): Promise<boolean>;
|
|
17
18
|
abstract clear(): Promise<boolean | void>;
|
|
@@ -5,15 +5,15 @@ import { ISplit } from '../dtos/types';
|
|
|
5
5
|
* to minimize the effort required to implement this interface.
|
|
6
6
|
*/
|
|
7
7
|
export declare abstract class AbstractSplitsCacheSync implements ISplitsCacheSync {
|
|
8
|
-
abstract addSplit(name: string, split:
|
|
9
|
-
addSplits(entries: [string,
|
|
8
|
+
abstract addSplit(name: string, split: ISplit): boolean;
|
|
9
|
+
addSplits(entries: [string, ISplit][]): boolean[];
|
|
10
10
|
abstract removeSplit(name: string): boolean;
|
|
11
11
|
removeSplits(names: string[]): boolean[];
|
|
12
|
-
abstract getSplit(name: string):
|
|
13
|
-
getSplits(names: string[]): Record<string,
|
|
12
|
+
abstract getSplit(name: string): ISplit | null;
|
|
13
|
+
getSplits(names: string[]): Record<string, ISplit | null>;
|
|
14
14
|
abstract setChangeNumber(changeNumber: number): boolean;
|
|
15
15
|
abstract getChangeNumber(): number;
|
|
16
|
-
getAll():
|
|
16
|
+
getAll(): ISplit[];
|
|
17
17
|
abstract getSplitNames(): string[];
|
|
18
18
|
abstract trafficTypeExists(trafficType: string): boolean;
|
|
19
19
|
abstract usesSegments(): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ISplitFiltersValidation } from '../../dtos/types';
|
|
1
|
+
import { ISplit, ISplitFiltersValidation } from '../../dtos/types';
|
|
2
2
|
import { AbstractSplitsCacheSync } from '../AbstractSplitsCacheSync';
|
|
3
3
|
import { KeyBuilderCS } from '../KeyBuilderCS';
|
|
4
4
|
import { ILogger } from '../../logger/types';
|
|
@@ -26,9 +26,9 @@ export declare class SplitsCacheInLocal extends AbstractSplitsCacheSync {
|
|
|
26
26
|
* We cannot simply call `localStorage.clear()` since that implies removing user items from the storage.
|
|
27
27
|
*/
|
|
28
28
|
clear(): void;
|
|
29
|
-
addSplit(name: string, split:
|
|
29
|
+
addSplit(name: string, split: ISplit): boolean;
|
|
30
30
|
removeSplit(name: string): boolean;
|
|
31
|
-
getSplit(name: string):
|
|
31
|
+
getSplit(name: string): any;
|
|
32
32
|
setChangeNumber(changeNumber: number): boolean;
|
|
33
33
|
getChangeNumber(): number;
|
|
34
34
|
getSplitNames(): string[];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ISplit } from '../../dtos/types';
|
|
1
2
|
import { AbstractSplitsCacheSync } from '../AbstractSplitsCacheSync';
|
|
2
3
|
/**
|
|
3
4
|
* Default ISplitsCacheSync implementation that stores split definitions in memory.
|
|
@@ -9,9 +10,9 @@ export declare class SplitsCacheInMemory extends AbstractSplitsCacheSync {
|
|
|
9
10
|
private changeNumber;
|
|
10
11
|
private splitsWithSegmentsCount;
|
|
11
12
|
clear(): void;
|
|
12
|
-
addSplit(name: string, split:
|
|
13
|
+
addSplit(name: string, split: ISplit): boolean;
|
|
13
14
|
removeSplit(name: string): boolean;
|
|
14
|
-
getSplit(name: string):
|
|
15
|
+
getSplit(name: string): ISplit | null;
|
|
15
16
|
setChangeNumber(changeNumber: number): boolean;
|
|
16
17
|
getChangeNumber(): number;
|
|
17
18
|
getSplitNames(): string[];
|
|
@@ -5,9 +5,10 @@ export declare class ImpressionCountsCacheInRedis extends ImpressionCountsCacheI
|
|
|
5
5
|
private readonly log;
|
|
6
6
|
private readonly key;
|
|
7
7
|
private readonly redis;
|
|
8
|
-
private
|
|
9
|
-
|
|
8
|
+
private readonly refreshRate;
|
|
9
|
+
private intervalId;
|
|
10
|
+
constructor(log: ILogger, key: string, redis: Redis, impressionCountsCacheSize?: number, refreshRate?: number);
|
|
10
11
|
postImpressionCountsInRedis(): Promise<boolean | import("ioredis").BooleanResponse | undefined>;
|
|
11
|
-
start(
|
|
12
|
-
stop():
|
|
12
|
+
start(): void;
|
|
13
|
+
stop(): Promise<boolean | import("ioredis").BooleanResponse | undefined>;
|
|
13
14
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { KeyBuilderSS } from '../KeyBuilderSS';
|
|
2
2
|
import { Redis } from 'ioredis';
|
|
3
3
|
import { ILogger } from '../../logger/types';
|
|
4
|
+
import { ISplit } from '../../dtos/types';
|
|
4
5
|
import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
|
|
5
6
|
/**
|
|
6
7
|
* ISplitsCacheAsync implementation that stores split definitions in Redis.
|
|
@@ -19,13 +20,13 @@ export declare class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
19
20
|
* The returned promise is resolved when the operation success
|
|
20
21
|
* or rejected if it fails (e.g., redis operation fails)
|
|
21
22
|
*/
|
|
22
|
-
addSplit(name: string, split:
|
|
23
|
+
addSplit(name: string, split: ISplit): Promise<boolean>;
|
|
23
24
|
/**
|
|
24
25
|
* Add a list of splits.
|
|
25
26
|
* The returned promise is resolved when the operation success
|
|
26
27
|
* or rejected if it fails (e.g., redis operation fails)
|
|
27
28
|
*/
|
|
28
|
-
addSplits(entries: [string,
|
|
29
|
+
addSplits(entries: [string, ISplit][]): Promise<boolean[]>;
|
|
29
30
|
/**
|
|
30
31
|
* Remove a given split.
|
|
31
32
|
* The returned promise is resolved when the operation success, with 1 or 0 indicating if the split existed or not.
|
|
@@ -42,7 +43,7 @@ export declare class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
42
43
|
* Get split definition or null if it's not defined.
|
|
43
44
|
* Returned promise is rejected if redis operation fails.
|
|
44
45
|
*/
|
|
45
|
-
getSplit(name: string): Promise<
|
|
46
|
+
getSplit(name: string): Promise<ISplit | null>;
|
|
46
47
|
/**
|
|
47
48
|
* Set till number.
|
|
48
49
|
* The returned promise is resolved when the operation success,
|
|
@@ -63,7 +64,7 @@ export declare class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
63
64
|
* @TODO we need to benchmark which is the maximun number of commands we could
|
|
64
65
|
* pipeline without kill redis performance.
|
|
65
66
|
*/
|
|
66
|
-
getAll(): Promise<
|
|
67
|
+
getAll(): Promise<ISplit[]>;
|
|
67
68
|
/**
|
|
68
69
|
* Get list of split names.
|
|
69
70
|
* The returned promise is resolved with the list of split names,
|
|
@@ -87,5 +88,5 @@ export declare class SplitsCacheInRedis extends AbstractSplitsCacheAsync {
|
|
|
87
88
|
* Fetches multiple splits definitions.
|
|
88
89
|
* Returned promise is rejected if redis operation fails.
|
|
89
90
|
*/
|
|
90
|
-
getSplits(names: string[]): Promise<Record<string,
|
|
91
|
+
getSplits(names: string[]): Promise<Record<string, ISplit | null>>;
|
|
91
92
|
}
|
|
@@ -6,9 +6,10 @@ export declare class UniqueKeysCacheInRedis extends UniqueKeysCacheInMemory impl
|
|
|
6
6
|
private readonly log;
|
|
7
7
|
private readonly key;
|
|
8
8
|
private readonly redis;
|
|
9
|
-
private
|
|
10
|
-
|
|
9
|
+
private readonly refreshRate;
|
|
10
|
+
private intervalId;
|
|
11
|
+
constructor(log: ILogger, key: string, redis: Redis, uniqueKeysQueueSize?: number, refreshRate?: number);
|
|
11
12
|
postUniqueKeysInRedis(): Promise<boolean | import("ioredis").BooleanResponse | undefined>;
|
|
12
|
-
start(
|
|
13
|
-
stop():
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): Promise<boolean | import("ioredis").BooleanResponse | undefined>;
|
|
14
15
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { KeyBuilder } from '../KeyBuilder';
|
|
2
2
|
import { IPluggableStorageWrapper } from '../types';
|
|
3
3
|
import { ILogger } from '../../logger/types';
|
|
4
|
+
import { ISplit } from '../../dtos/types';
|
|
4
5
|
import { AbstractSplitsCacheAsync } from '../AbstractSplitsCacheAsync';
|
|
5
6
|
/**
|
|
6
7
|
* ISplitsCacheAsync implementation for pluggable storages.
|
|
@@ -23,13 +24,13 @@ export declare class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
23
24
|
* The returned promise is resolved when the operation success
|
|
24
25
|
* or rejected if it fails (e.g., wrapper operation fails)
|
|
25
26
|
*/
|
|
26
|
-
addSplit(name: string, split:
|
|
27
|
+
addSplit(name: string, split: ISplit): Promise<boolean>;
|
|
27
28
|
/**
|
|
28
29
|
* Add a list of splits.
|
|
29
30
|
* The returned promise is resolved when the operation success
|
|
30
31
|
* or rejected if it fails (e.g., wrapper operation fails)
|
|
31
32
|
*/
|
|
32
|
-
addSplits(entries: [string,
|
|
33
|
+
addSplits(entries: [string, ISplit][]): Promise<boolean[]>;
|
|
33
34
|
/**
|
|
34
35
|
* Remove a given split.
|
|
35
36
|
* The returned promise is resolved when the operation success, with a boolean indicating if the split existed or not.
|
|
@@ -47,19 +48,19 @@ export declare class SplitsCachePluggable extends AbstractSplitsCacheAsync {
|
|
|
47
48
|
* The returned promise is resolved with the split definition or null if it's not defined,
|
|
48
49
|
* or rejected if wrapper operation fails.
|
|
49
50
|
*/
|
|
50
|
-
getSplit(name: string): Promise<
|
|
51
|
+
getSplit(name: string): Promise<ISplit | null>;
|
|
51
52
|
/**
|
|
52
53
|
* Get list of splits.
|
|
53
54
|
* The returned promise is resolved with a map of split names to their split definition or null if it's not defined,
|
|
54
55
|
* or rejected if wrapper operation fails.
|
|
55
56
|
*/
|
|
56
|
-
getSplits(names: string[]): Promise<Record<string,
|
|
57
|
+
getSplits(names: string[]): Promise<Record<string, ISplit | null>>;
|
|
57
58
|
/**
|
|
58
59
|
* Get list of all split definitions.
|
|
59
60
|
* The returned promise is resolved with the list of split definitions,
|
|
60
61
|
* or rejected if wrapper operation fails.
|
|
61
62
|
*/
|
|
62
|
-
getAll(): Promise<
|
|
63
|
+
getAll(): Promise<ISplit[]>;
|
|
63
64
|
/**
|
|
64
65
|
* Get list of split names.
|
|
65
66
|
* The returned promise is resolved with the list of split names,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MaybeThenable, IMetadata, ISplitFiltersValidation } from '../dtos/types';
|
|
1
|
+
import { MaybeThenable, IMetadata, ISplitFiltersValidation, ISplit } from '../dtos/types';
|
|
2
2
|
import { ILogger } from '../logger/types';
|
|
3
3
|
import { EventDataType, HttpErrors, HttpLatencies, ImpressionDataType, LastSync, Method, MethodExceptions, MethodLatencies, OperationType, StoredEventWithMetadata, StoredImpressionWithMetadata, StreamingEvent, UniqueKeysPayloadCs, UniqueKeysPayloadSs } from '../sync/submitters/types';
|
|
4
4
|
import { SplitIO, ImpressionDTO, SDKMode } from '../types';
|
|
@@ -178,13 +178,13 @@ export interface IPluggableStorageWrapper {
|
|
|
178
178
|
}
|
|
179
179
|
/** Splits cache */
|
|
180
180
|
export interface ISplitsCacheBase {
|
|
181
|
-
addSplits(entries: [string,
|
|
181
|
+
addSplits(entries: [string, ISplit][]): MaybeThenable<boolean[] | void>;
|
|
182
182
|
removeSplits(names: string[]): MaybeThenable<boolean[] | void>;
|
|
183
|
-
getSplit(name: string): MaybeThenable<
|
|
184
|
-
getSplits(names: string[]): MaybeThenable<Record<string,
|
|
183
|
+
getSplit(name: string): MaybeThenable<ISplit | null>;
|
|
184
|
+
getSplits(names: string[]): MaybeThenable<Record<string, ISplit | null>>;
|
|
185
185
|
setChangeNumber(changeNumber: number): MaybeThenable<boolean | void>;
|
|
186
186
|
getChangeNumber(): MaybeThenable<number>;
|
|
187
|
-
getAll(): MaybeThenable<
|
|
187
|
+
getAll(): MaybeThenable<ISplit[]>;
|
|
188
188
|
getSplitNames(): MaybeThenable<string[]>;
|
|
189
189
|
trafficTypeExists(trafficType: string): MaybeThenable<boolean>;
|
|
190
190
|
usesSegments(): MaybeThenable<boolean>;
|
|
@@ -193,13 +193,13 @@ export interface ISplitsCacheBase {
|
|
|
193
193
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): MaybeThenable<boolean>;
|
|
194
194
|
}
|
|
195
195
|
export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
196
|
-
addSplits(entries: [string,
|
|
196
|
+
addSplits(entries: [string, ISplit][]): boolean[];
|
|
197
197
|
removeSplits(names: string[]): boolean[];
|
|
198
|
-
getSplit(name: string):
|
|
199
|
-
getSplits(names: string[]): Record<string,
|
|
198
|
+
getSplit(name: string): ISplit | null;
|
|
199
|
+
getSplits(names: string[]): Record<string, ISplit | null>;
|
|
200
200
|
setChangeNumber(changeNumber: number): boolean;
|
|
201
201
|
getChangeNumber(): number;
|
|
202
|
-
getAll():
|
|
202
|
+
getAll(): ISplit[];
|
|
203
203
|
getSplitNames(): string[];
|
|
204
204
|
trafficTypeExists(trafficType: string): boolean;
|
|
205
205
|
usesSegments(): boolean;
|
|
@@ -208,13 +208,13 @@ export interface ISplitsCacheSync extends ISplitsCacheBase {
|
|
|
208
208
|
killLocally(name: string, defaultTreatment: string, changeNumber: number): boolean;
|
|
209
209
|
}
|
|
210
210
|
export interface ISplitsCacheAsync extends ISplitsCacheBase {
|
|
211
|
-
addSplits(entries: [string,
|
|
211
|
+
addSplits(entries: [string, ISplit][]): Promise<boolean[] | void>;
|
|
212
212
|
removeSplits(names: string[]): Promise<boolean[] | void>;
|
|
213
|
-
getSplit(name: string): Promise<
|
|
214
|
-
getSplits(names: string[]): Promise<Record<string,
|
|
213
|
+
getSplit(name: string): Promise<ISplit | null>;
|
|
214
|
+
getSplits(names: string[]): Promise<Record<string, ISplit | null>>;
|
|
215
215
|
setChangeNumber(changeNumber: number): Promise<boolean | void>;
|
|
216
216
|
getChangeNumber(): Promise<number>;
|
|
217
|
-
getAll(): Promise<
|
|
217
|
+
getAll(): Promise<ISplit[]>;
|
|
218
218
|
getSplitNames(): Promise<string[]>;
|
|
219
219
|
trafficTypeExists(trafficType: string): Promise<boolean>;
|
|
220
220
|
usesSegments(): Promise<boolean>;
|