@splitsoftware/splitio-commons 1.1.0 → 1.2.1-rc.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 +3 -0
- package/cjs/evaluator/matchers/ew.js +3 -3
- package/cjs/logger/messages/info.js +3 -3
- package/cjs/sdkClient/client.js +2 -1
- package/cjs/sdkClient/clientAttributesDecoration.js +108 -0
- package/cjs/sdkClient/clientCS.js +10 -7
- package/cjs/sdkClient/sdkClientMethodCS.js +2 -2
- package/cjs/sdkClient/sdkClientMethodCSWithTT.js +2 -2
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/inMemory/AttributesCacheInMemory.js +70 -0
- package/cjs/sync/polling/fetchers/mySegmentsFetcher.js +2 -2
- package/cjs/sync/polling/pollingManagerCS.js +2 -1
- package/cjs/sync/polling/pollingManagerSS.js +2 -1
- package/cjs/sync/polling/syncTasks/mySegmentsSyncTask.js +1 -1
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/cjs/sync/streaming/AuthClient/index.js +1 -2
- package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +1 -1
- package/cjs/sync/streaming/pushManager.js +27 -23
- package/cjs/sync/submitters/submitterManager.js +2 -1
- package/cjs/sync/syncManagerOnline.js +12 -12
- package/cjs/utils/inputValidation/attribute.js +20 -0
- package/cjs/utils/inputValidation/attributes.js +13 -1
- package/cjs/utils/inputValidation/eventProperties.js +3 -1
- package/cjs/utils/lang/index.js +1 -13
- package/cjs/utils/murmur3/legacy.js +44 -0
- package/cjs/utils/promise/timeout.js +1 -1
- package/esm/evaluator/matchers/ew.js +4 -4
- package/esm/logger/messages/info.js +3 -3
- package/esm/sdkClient/client.js +2 -1
- package/esm/sdkClient/clientAttributesDecoration.js +104 -0
- package/esm/sdkClient/clientCS.js +10 -7
- package/esm/sdkClient/sdkClientMethodCS.js +2 -2
- package/esm/sdkClient/sdkClientMethodCSWithTT.js +2 -2
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/storages/inMemory/AttributesCacheInMemory.js +67 -0
- package/esm/sync/polling/fetchers/mySegmentsFetcher.js +2 -2
- package/esm/sync/polling/pollingManagerCS.js +2 -1
- package/esm/sync/polling/pollingManagerSS.js +2 -1
- package/esm/sync/polling/syncTasks/mySegmentsSyncTask.js +1 -1
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +2 -2
- package/esm/sync/streaming/AuthClient/index.js +1 -2
- package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +1 -1
- package/esm/sync/streaming/pushManager.js +27 -23
- package/esm/sync/submitters/submitterManager.js +2 -1
- package/esm/sync/syncManagerOnline.js +12 -12
- package/esm/utils/inputValidation/attribute.js +16 -0
- package/esm/utils/inputValidation/attributes.js +11 -0
- package/esm/utils/inputValidation/eventProperties.js +4 -2
- package/esm/utils/lang/index.js +0 -11
- package/esm/utils/murmur3/legacy.js +39 -0
- package/esm/utils/promise/timeout.js +1 -1
- package/package.json +4 -4
- package/src/evaluator/matchers/ew.ts +4 -4
- package/src/logger/messages/info.ts +3 -3
- package/src/sdkClient/client.ts +2 -1
- package/src/sdkClient/clientAttributesDecoration.ts +122 -0
- package/src/sdkClient/clientCS.ts +14 -7
- package/src/sdkClient/sdkClientMethodCS.ts +2 -0
- package/src/sdkClient/sdkClientMethodCSWithTT.ts +2 -0
- package/src/services/splitHttpClient.ts +1 -1
- package/src/storages/inMemory/AttributesCacheInMemory.ts +73 -0
- package/src/sync/polling/fetchers/mySegmentsFetcher.ts +2 -1
- package/src/sync/polling/fetchers/types.ts +1 -0
- package/src/sync/polling/pollingManagerCS.ts +3 -6
- package/src/sync/polling/pollingManagerSS.ts +3 -8
- package/src/sync/polling/syncTasks/mySegmentsSyncTask.ts +2 -1
- package/src/sync/polling/types.ts +0 -12
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +2 -1
- package/src/sync/streaming/AuthClient/index.ts +1 -2
- package/src/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.ts +1 -1
- package/src/sync/streaming/pushManager.ts +31 -38
- package/src/sync/streaming/types.ts +5 -25
- package/src/sync/submitters/submitterManager.ts +4 -8
- package/src/sync/syncManagerOnline.ts +16 -22
- package/src/types.ts +43 -0
- package/src/utils/inputValidation/attribute.ts +21 -0
- package/src/utils/inputValidation/attributes.ts +14 -0
- package/src/utils/inputValidation/eventProperties.ts +4 -2
- package/src/utils/lang/index.ts +0 -14
- package/src/utils/murmur3/legacy.ts +48 -0
- package/src/utils/promise/timeout.ts +1 -1
- package/types/sdkClient/clientAttributesDecoration.d.ts +51 -0
- package/types/sdkClient/clientCS.d.ts +2 -1
- package/types/storages/inMemory/AttributesCacheInMemory.d.ts +43 -0
- package/types/storages/inMemory/TelemetryCacheInMemory.d.ts +51 -0
- package/types/storages/inMemory/index.d.ts +10 -0
- package/types/storages/inRedis/TelemetryCacheInRedis.d.ts +0 -0
- package/types/storages/parseSegments.d.ts +6 -0
- package/types/storages/pluggable/TelemetryCachePluggable.d.ts +2 -0
- package/types/sync/polling/fetchers/mySegmentsFetcher.d.ts +1 -1
- package/types/sync/polling/fetchers/types.d.ts +1 -1
- package/types/sync/polling/pollingManagerCS.d.ts +2 -5
- package/types/sync/polling/pollingManagerSS.d.ts +2 -5
- package/types/sync/polling/types.d.ts +0 -11
- package/types/sync/polling/updaters/mySegmentsUpdater.d.ts +1 -1
- package/types/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.d.ts +1 -1
- package/types/sync/streaming/pushManager.d.ts +3 -7
- package/types/sync/streaming/pushManagerCS.d.ts +1 -12
- package/types/sync/streaming/pushManagerSS.d.ts +1 -11
- package/types/sync/streaming/types.d.ts +3 -23
- package/types/sync/submitters/submitterManager.d.ts +2 -4
- package/types/sync/submitters/telemetrySyncTask.d.ts +17 -0
- package/types/sync/syncManagerOnline.d.ts +3 -3
- package/types/trackers/telemetryRecorder.d.ts +0 -0
- package/types/types.d.ts +40 -0
- package/types/utils/EventEmitter.d.ts +4 -0
- package/types/utils/inputValidation/attribute.d.ts +2 -0
- package/types/utils/inputValidation/attributes.d.ts +1 -0
- package/types/utils/lang/index.d.ts +0 -4
- package/types/utils/murmur3/legacy.d.ts +2 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from '../lang';
|
|
2
|
+
import { validateAttribute } from './attribute';
|
|
2
3
|
import { ERROR_NOT_PLAIN_OBJECT } from '../../logger/constants';
|
|
3
4
|
export function validateAttributes(log, maybeAttrs, method) {
|
|
4
5
|
// Attributes are optional
|
|
@@ -7,3 +8,13 @@ export function validateAttributes(log, maybeAttrs, method) {
|
|
|
7
8
|
log.error(ERROR_NOT_PLAIN_OBJECT, [method, 'attributes']);
|
|
8
9
|
return false;
|
|
9
10
|
}
|
|
11
|
+
export function validateAttributesDeep(log, maybeAttributes, method) {
|
|
12
|
+
if (!validateAttributes(log, maybeAttributes, method))
|
|
13
|
+
return false;
|
|
14
|
+
var result = true;
|
|
15
|
+
Object.keys(maybeAttributes).forEach(function (attributeKey) {
|
|
16
|
+
if (!validateAttribute(log, attributeKey, maybeAttributes[attributeKey], method))
|
|
17
|
+
result = false;
|
|
18
|
+
});
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { isObject,
|
|
1
|
+
import { isObject, isString, isFiniteNumber, isBoolean } from '../lang';
|
|
2
|
+
import { objectAssign } from '../lang/objectAssign';
|
|
2
3
|
import { ERROR_NOT_PLAIN_OBJECT, ERROR_SIZE_EXCEEDED, WARN_SETTING_NULL, WARN_TRIMMING_PROPERTIES } from '../../logger/constants';
|
|
3
4
|
var ECMA_SIZES = {
|
|
4
5
|
NULL: 0,
|
|
@@ -17,7 +18,8 @@ export function validateEventProperties(log, maybeProperties, method) {
|
|
|
17
18
|
return { properties: false, size: BASE_EVENT_SIZE };
|
|
18
19
|
}
|
|
19
20
|
var keys = Object.keys(maybeProperties);
|
|
20
|
-
|
|
21
|
+
// Shallow clone
|
|
22
|
+
var clone = objectAssign({}, maybeProperties);
|
|
21
23
|
// To avoid calculating the size twice we'll return it from here.
|
|
22
24
|
var output = {
|
|
23
25
|
properties: clone,
|
package/esm/utils/lang/index.js
CHANGED
|
@@ -183,17 +183,6 @@ export function merge(target, source) {
|
|
|
183
183
|
}
|
|
184
184
|
return res;
|
|
185
185
|
}
|
|
186
|
-
/**
|
|
187
|
-
* Shallow clone an object
|
|
188
|
-
*/
|
|
189
|
-
export function shallowClone(obj) {
|
|
190
|
-
var keys = Object.keys(obj);
|
|
191
|
-
var output = {};
|
|
192
|
-
for (var i = 0; i < keys.length; i++) {
|
|
193
|
-
output[keys[i]] = obj[keys[i]];
|
|
194
|
-
}
|
|
195
|
-
return output;
|
|
196
|
-
}
|
|
197
186
|
/**
|
|
198
187
|
* Checks if the target string starts with the sub string.
|
|
199
188
|
*/
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Deprecated hashing function, used for split bucketing. Replaced by murmur3
|
|
2
|
+
//
|
|
3
|
+
// JAVA reference implementation for the hashing function.
|
|
4
|
+
//
|
|
5
|
+
// int h = 0;
|
|
6
|
+
// for (int i = 0; i < key.length(); i++) {
|
|
7
|
+
// h = 31 * h + key.charAt(i);
|
|
8
|
+
// }
|
|
9
|
+
// return h ^ seed; // XOR the hash and seed
|
|
10
|
+
//
|
|
11
|
+
function ToInteger(x) {
|
|
12
|
+
x = Number(x);
|
|
13
|
+
return x < 0 ? Math.ceil(x) : Math.floor(x);
|
|
14
|
+
}
|
|
15
|
+
function modulo(a, b) {
|
|
16
|
+
return a - Math.floor(a / b) * b;
|
|
17
|
+
}
|
|
18
|
+
function ToUint32(x) {
|
|
19
|
+
return modulo(ToInteger(x), Math.pow(2, 32));
|
|
20
|
+
}
|
|
21
|
+
function ToInt32(x) {
|
|
22
|
+
var uint32 = ToUint32(x);
|
|
23
|
+
if (uint32 >= Math.pow(2, 31)) {
|
|
24
|
+
return uint32 - Math.pow(2, 32);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
return uint32;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function hash(str, seed) {
|
|
31
|
+
var h = 0;
|
|
32
|
+
for (var i = 0; i < str.length; i++) {
|
|
33
|
+
h = ToInt32(ToInt32(31 * h) + str.charCodeAt(i));
|
|
34
|
+
}
|
|
35
|
+
return ToInt32(h ^ seed);
|
|
36
|
+
}
|
|
37
|
+
export function bucket(str, seed) {
|
|
38
|
+
return Math.abs(hash(str, seed) % 100) + 1;
|
|
39
|
+
}
|
|
@@ -3,7 +3,7 @@ export function timeout(ms, promise) {
|
|
|
3
3
|
return promise;
|
|
4
4
|
return new Promise(function (resolve, reject) {
|
|
5
5
|
var tid = setTimeout(function () {
|
|
6
|
-
reject(new Error("Operation timed out because it exceeded the configured time limit of " + ms + "ms."));
|
|
6
|
+
reject(new Error("Operation timed out because it exceeded the configured time limit of " + ms + " ms."));
|
|
7
7
|
}, ms);
|
|
8
8
|
promise.then(function (res) {
|
|
9
9
|
clearTimeout(tid);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitsoftware/splitio-commons",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.2.1-rc.1",
|
|
4
4
|
"description": "Split Javascript SDK common components",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"build:cjs": "rimraf cjs && tsc -m CommonJS --outDir cjs --importHelpers",
|
|
25
25
|
"test": "jest",
|
|
26
26
|
"test:coverage": "jest --coverage",
|
|
27
|
-
"publish:rc": "npm run check && npm run
|
|
28
|
-
"publish:stable": "npm run check && npm run
|
|
27
|
+
"publish:rc": "npm run check && npm run build && npm run test && npm publish --tag rc",
|
|
28
|
+
"publish:stable": "npm run check && npm run build && npm run test && npm publish"
|
|
29
29
|
},
|
|
30
30
|
"repository": {
|
|
31
31
|
"type": "git",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"jest-localstorage-mock": "^2.4.3",
|
|
65
65
|
"js-yaml": "^3.14.0",
|
|
66
66
|
"lodash": "^4.17.21",
|
|
67
|
-
"node-fetch": "^2.6.
|
|
67
|
+
"node-fetch": "^2.6.7",
|
|
68
68
|
"redis-server": "1.2.2",
|
|
69
69
|
"rimraf": "^3.0.2",
|
|
70
70
|
"ts-jest": "^27.0.5",
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { ENGINE_MATCHER_ENDS_WITH } from '../../logger/constants';
|
|
2
2
|
import { ILogger } from '../../logger/types';
|
|
3
|
-
import { endsWith
|
|
3
|
+
import { endsWith } from '../../utils/lang';
|
|
4
4
|
|
|
5
5
|
export function endsWithMatcherContext(log: ILogger, ruleAttr: string[]) /*: Function */ {
|
|
6
6
|
return function endsWithMatcher(runtimeAttr: string): boolean {
|
|
7
|
-
let
|
|
7
|
+
let strEndsWith = ruleAttr.some(e => endsWith(runtimeAttr, e));
|
|
8
8
|
|
|
9
|
-
log.debug(ENGINE_MATCHER_ENDS_WITH, [runtimeAttr, ruleAttr,
|
|
9
|
+
log.debug(ENGINE_MATCHER_ENDS_WITH, [runtimeAttr, ruleAttr, strEndsWith]);
|
|
10
10
|
|
|
11
|
-
return
|
|
11
|
+
return strEndsWith;
|
|
12
12
|
};
|
|
13
13
|
}
|
|
@@ -23,10 +23,10 @@ export const codesInfo: [number, string][] = codesWarn.concat([
|
|
|
23
23
|
[c.SUBMITTERS_PUSH_FULL_EVENTS_QUEUE, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Flushing full events queue and reseting timer.'],
|
|
24
24
|
[c.SUBMITTERS_PUSH, c.LOG_PREFIX_SYNC_SUBMITTERS + 'Pushing %s %s.'],
|
|
25
25
|
[c.STREAMING_REFRESH_TOKEN, c.LOG_PREFIX_SYNC_STREAMING + 'Refreshing streaming token in %s seconds, and connecting streaming in %s seconds.'],
|
|
26
|
-
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect in %s seconds.'],
|
|
27
|
-
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting
|
|
26
|
+
[c.STREAMING_RECONNECT, c.LOG_PREFIX_SYNC_STREAMING + 'Attempting to reconnect streaming in %s seconds.'],
|
|
27
|
+
[c.STREAMING_CONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Connecting streaming.'],
|
|
28
28
|
[c.STREAMING_DISABLED, c.LOG_PREFIX_SYNC_STREAMING + 'Streaming is disabled for given Api key. Switching to polling mode.'],
|
|
29
|
-
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting
|
|
29
|
+
[c.STREAMING_DISCONNECTING, c.LOG_PREFIX_SYNC_STREAMING + 'Disconnecting streaming.'],
|
|
30
30
|
[c.SYNC_START_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming not available. Starting polling.'],
|
|
31
31
|
[c.SYNC_CONTINUE_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming couldn\'t connect. Continue polling.'],
|
|
32
32
|
[c.SYNC_STOP_POLLING, c.LOG_PREFIX_SYNC_MANAGER + 'Streaming (re)connected. Syncing and stopping polling.'],
|
package/src/sdkClient/client.ts
CHANGED
|
@@ -123,6 +123,7 @@ export function clientFactory(params: IClientFactoryParams): SplitIO.IClient | S
|
|
|
123
123
|
getTreatmentWithConfig,
|
|
124
124
|
getTreatments,
|
|
125
125
|
getTreatmentsWithConfig,
|
|
126
|
-
track
|
|
126
|
+
track,
|
|
127
|
+
isBrowserClient: false
|
|
127
128
|
} as SplitIO.IClient | SplitIO.IAsyncClient;
|
|
128
129
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { AttributesCacheInMemory } from '../storages/inMemory/AttributesCacheInMemory';
|
|
2
|
+
import { validateAttributesDeep } from '../utils/inputValidation/attributes';
|
|
3
|
+
import { SplitIO } from '../types';
|
|
4
|
+
import { ILogger } from '../logger/types';
|
|
5
|
+
import { objectAssign } from '../utils/lang/objectAssign';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Add in memory attributes storage methods and combine them with any attribute received from the getTreatment/s call
|
|
9
|
+
*/
|
|
10
|
+
export function clientAttributesDecoration<TClient extends SplitIO.IClient | SplitIO.IAsyncClient>(log: ILogger, client: TClient) {
|
|
11
|
+
|
|
12
|
+
const attributeStorage = new AttributesCacheInMemory();
|
|
13
|
+
|
|
14
|
+
// Keep a reference to the original methods
|
|
15
|
+
const clientGetTreatment = client.getTreatment;
|
|
16
|
+
const clientGetTreatmentWithConfig = client.getTreatmentWithConfig;
|
|
17
|
+
const clientGetTreatments = client.getTreatments;
|
|
18
|
+
const clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
|
|
19
|
+
const clientTrack = client.track;
|
|
20
|
+
|
|
21
|
+
function getTreatment(maybeKey: SplitIO.SplitKey, maybeSplit: string, maybeAttributes?: SplitIO.Attributes) {
|
|
22
|
+
return clientGetTreatment(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getTreatmentWithConfig(maybeKey: SplitIO.SplitKey, maybeSplit: string, maybeAttributes?: SplitIO.Attributes) {
|
|
26
|
+
return clientGetTreatmentWithConfig(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getTreatments(maybeKey: SplitIO.SplitKey, maybeSplits: string[], maybeAttributes?: SplitIO.Attributes) {
|
|
30
|
+
return clientGetTreatments(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getTreatmentsWithConfig(maybeKey: SplitIO.SplitKey, maybeSplits: string[], maybeAttributes?: SplitIO.Attributes) {
|
|
34
|
+
return clientGetTreatmentsWithConfig(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function track(maybeKey: SplitIO.SplitKey, maybeTT: string, maybeEvent: string, maybeEventValue?: number, maybeProperties?: SplitIO.Properties) {
|
|
38
|
+
return clientTrack(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function combineAttributes(maybeAttributes: SplitIO.Attributes | undefined): SplitIO.Attributes | undefined{
|
|
42
|
+
const storedAttributes = attributeStorage.getAll();
|
|
43
|
+
if (Object.keys(storedAttributes).length > 0) {
|
|
44
|
+
return objectAssign({}, storedAttributes, maybeAttributes);
|
|
45
|
+
}
|
|
46
|
+
return maybeAttributes;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return objectAssign(client, {
|
|
50
|
+
getTreatment: getTreatment,
|
|
51
|
+
getTreatmentWithConfig: getTreatmentWithConfig,
|
|
52
|
+
getTreatments: getTreatments,
|
|
53
|
+
getTreatmentsWithConfig: getTreatmentsWithConfig,
|
|
54
|
+
track: track,
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Add an attribute to client's in memory attributes storage
|
|
58
|
+
*
|
|
59
|
+
* @param {string} attributeName Attrinute name
|
|
60
|
+
* @param {string, number, boolean, list} attributeValue Attribute value
|
|
61
|
+
* @returns {boolean} true if the attribute was stored and false otherways
|
|
62
|
+
*/
|
|
63
|
+
setAttribute(attributeName: string, attributeValue: Object) {
|
|
64
|
+
const attribute: Record<string, Object> = {};
|
|
65
|
+
attribute[attributeName] = attributeValue;
|
|
66
|
+
if (!validateAttributesDeep(log, attribute, 'setAttribute')) return false;
|
|
67
|
+
log.debug(`stored ${attributeValue} for attribute ${attributeName}`);
|
|
68
|
+
return attributeStorage.setAttribute(attributeName, attributeValue);
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Returns the attribute with the given key
|
|
73
|
+
*
|
|
74
|
+
* @param {string} attributeName Attribute name
|
|
75
|
+
* @returns {Object} Attribute with the given key
|
|
76
|
+
*/
|
|
77
|
+
getAttribute(attributeName: string) {
|
|
78
|
+
log.debug(`retrieved attribute ${attributeName}`);
|
|
79
|
+
return attributeStorage.getAttribute(attributeName + '');
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Add to client's in memory attributes storage the attributes in 'attributes'
|
|
84
|
+
*
|
|
85
|
+
* @param {Object} attributes Object with attributes to store
|
|
86
|
+
* @returns true if attributes were stored an false otherways
|
|
87
|
+
*/
|
|
88
|
+
setAttributes(attributes: Record<string, Object>) {
|
|
89
|
+
if (!validateAttributesDeep(log, attributes, 'setAttributes')) return false;
|
|
90
|
+
return attributeStorage.setAttributes(attributes);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Return all the attributes stored in client's in memory attributes storage
|
|
95
|
+
*
|
|
96
|
+
* @returns {Object} returns all the stored attributes
|
|
97
|
+
*/
|
|
98
|
+
getAttributes(): Record<string, Object> {
|
|
99
|
+
return attributeStorage.getAll();
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Removes from client's in memory attributes storage the attribute with the given key
|
|
104
|
+
*
|
|
105
|
+
* @param {string} attributeName
|
|
106
|
+
* @returns {boolean} true if attribute was removed and false otherways
|
|
107
|
+
*/
|
|
108
|
+
removeAttribute(attributeName: string) {
|
|
109
|
+
log.debug(`removed attribute ${attributeName}`);
|
|
110
|
+
return attributeStorage.removeAttribute(attributeName + '');
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Remove all the stored attributes in the client's in memory attribute storage
|
|
115
|
+
*/
|
|
116
|
+
clearAttributes() {
|
|
117
|
+
return attributeStorage.clear();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
|
+
import { ILogger } from '../logger/types';
|
|
2
3
|
import { SplitIO } from '../types';
|
|
4
|
+
import { clientAttributesDecoration } from './clientAttributesDecoration';
|
|
3
5
|
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -9,15 +11,20 @@ import { SplitIO } from '../types';
|
|
|
9
11
|
* @param key validated split key
|
|
10
12
|
* @param trafficType validated traffic type
|
|
11
13
|
*/
|
|
12
|
-
export function clientCSDecorator(client: SplitIO.IClient, key: SplitIO.SplitKey, trafficType?: string): SplitIO.ICsClient {
|
|
13
|
-
|
|
14
|
+
export function clientCSDecorator(log: ILogger, client: SplitIO.IClient, key: SplitIO.SplitKey, trafficType?: string): SplitIO.ICsClient {
|
|
15
|
+
|
|
16
|
+
let clientCS = clientAttributesDecoration(log, client);
|
|
17
|
+
|
|
18
|
+
return objectAssign(clientCS, {
|
|
14
19
|
// In the client-side API, we bind a key to the client `getTreatment*` methods
|
|
15
|
-
getTreatment:
|
|
16
|
-
getTreatmentWithConfig:
|
|
17
|
-
getTreatments:
|
|
18
|
-
getTreatmentsWithConfig:
|
|
20
|
+
getTreatment: clientCS.getTreatment.bind(clientCS, key),
|
|
21
|
+
getTreatmentWithConfig: clientCS.getTreatmentWithConfig.bind(clientCS, key),
|
|
22
|
+
getTreatments: clientCS.getTreatments.bind(clientCS, key),
|
|
23
|
+
getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
|
|
19
24
|
|
|
20
25
|
// Key is bound to the `track` method. Same thing happens with trafficType but only if provided
|
|
21
|
-
track: trafficType ?
|
|
26
|
+
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key),
|
|
27
|
+
|
|
28
|
+
isBrowserClient: true
|
|
22
29
|
}) as SplitIO.ICsClient;
|
|
23
30
|
}
|
|
@@ -29,6 +29,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
|
|
|
29
29
|
const validKey = validateKey(log, key, method);
|
|
30
30
|
|
|
31
31
|
const mainClientInstance = clientCSDecorator(
|
|
32
|
+
log,
|
|
32
33
|
sdkClientFactory(params) as SplitIO.IClient, // @ts-ignore
|
|
33
34
|
validKey
|
|
34
35
|
);
|
|
@@ -74,6 +75,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
|
|
|
74
75
|
// As shared clients reuse all the storage information, we don't need to check here if we
|
|
75
76
|
// will use offline or online mode. We should stick with the original decision.
|
|
76
77
|
clientInstances[instanceId] = clientCSDecorator(
|
|
78
|
+
log,
|
|
77
79
|
sdkClientFactory(objectAssign({}, params, {
|
|
78
80
|
sdkReadinessManager: sharedSdkReadiness,
|
|
79
81
|
storage: sharedStorage || storage,
|
|
@@ -35,6 +35,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const mainClientInstance = clientCSDecorator(
|
|
38
|
+
log,
|
|
38
39
|
sdkClientFactory(params) as SplitIO.IClient, // @ts-ignore
|
|
39
40
|
validKey,
|
|
40
41
|
validTrafficType
|
|
@@ -88,6 +89,7 @@ export function sdkClientMethodCSFactory(params: ISdkClientFactoryParams): (key?
|
|
|
88
89
|
// As shared clients reuse all the storage information, we don't need to check here if we
|
|
89
90
|
// will use offline or online mode. We should stick with the original decision.
|
|
90
91
|
clientInstances[instanceId] = clientCSDecorator(
|
|
92
|
+
log,
|
|
91
93
|
sdkClientFactory(objectAssign({}, params, {
|
|
92
94
|
sdkReadinessManager: sharedSdkReadiness,
|
|
93
95
|
storage: sharedStorage || storage,
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { objectAssign } from '../../utils/lang/objectAssign';
|
|
2
|
+
|
|
3
|
+
export class AttributesCacheInMemory {
|
|
4
|
+
|
|
5
|
+
private attributesCache: Record<string, Object> = {};
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Create or update the value for the given attribute
|
|
10
|
+
*
|
|
11
|
+
* @param {string} attributeName attribute name
|
|
12
|
+
* @param {Object} attributeValue attribute value
|
|
13
|
+
* @returns {boolean} the attribute was stored
|
|
14
|
+
*/
|
|
15
|
+
setAttribute(attributeName: string, attributeValue: Object): boolean {
|
|
16
|
+
this.attributesCache[attributeName] = attributeValue;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Retrieves the value of a given attribute
|
|
22
|
+
*
|
|
23
|
+
* @param {string} attributeName attribute name
|
|
24
|
+
* @returns {Object?} stored attribute value
|
|
25
|
+
*/
|
|
26
|
+
getAttribute(attributeName: string): Object {
|
|
27
|
+
return this.attributesCache[attributeName];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create or update all the given attributes
|
|
32
|
+
*
|
|
33
|
+
* @param {[string, Object]} attributes attributes to create or update
|
|
34
|
+
* @returns {boolean} attributes were stored
|
|
35
|
+
*/
|
|
36
|
+
setAttributes(attributes: Record<string, Object>): boolean {
|
|
37
|
+
this.attributesCache = objectAssign(this.attributesCache, attributes);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Retrieve the full attributes map
|
|
43
|
+
*
|
|
44
|
+
* @returns {Map<string, Object>} stored attributes
|
|
45
|
+
*/
|
|
46
|
+
getAll(): Record<string, Object> {
|
|
47
|
+
return this.attributesCache;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Removes a given attribute from the map
|
|
52
|
+
*
|
|
53
|
+
* @param {string} attributeName attribute to remove
|
|
54
|
+
* @returns {boolean} attribute removed
|
|
55
|
+
*/
|
|
56
|
+
removeAttribute(attributeName: string): boolean {
|
|
57
|
+
if (Object.keys(this.attributesCache).indexOf(attributeName) >= 0) {
|
|
58
|
+
delete this.attributesCache[attributeName];
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Clears all attributes stored in the SDK
|
|
66
|
+
*
|
|
67
|
+
*/
|
|
68
|
+
clear() {
|
|
69
|
+
this.attributesCache = {};
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
}
|
|
@@ -6,9 +6,10 @@ import { IMySegmentsFetcher } from './types';
|
|
|
6
6
|
* Factory of MySegments fetcher.
|
|
7
7
|
* MySegments fetcher is a wrapper around `mySegments` API service that parses the response and handle errors.
|
|
8
8
|
*/
|
|
9
|
-
export function mySegmentsFetcherFactory(fetchMySegments: IFetchMySegments
|
|
9
|
+
export function mySegmentsFetcherFactory(fetchMySegments: IFetchMySegments): IMySegmentsFetcher {
|
|
10
10
|
|
|
11
11
|
return function mySegmentsFetcher(
|
|
12
|
+
userMatchingKey: string,
|
|
12
13
|
noCache?: boolean,
|
|
13
14
|
// Optional decorator for `fetchMySegments` promise, such as timeout or time tracker
|
|
14
15
|
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
|
|
@@ -15,6 +15,7 @@ export type ISegmentChangesFetcher = (
|
|
|
15
15
|
) => Promise<ISegmentChangesResponse[]>
|
|
16
16
|
|
|
17
17
|
export type IMySegmentsFetcher = (
|
|
18
|
+
userMatchingKey: string,
|
|
18
19
|
noCache?: boolean,
|
|
19
20
|
decorator?: (promise: Promise<IResponse>) => Promise<IResponse>
|
|
20
21
|
) => Promise<string[]>
|
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
import { ISegmentsSyncTask, ISplitsSyncTask, IPollingManagerCS } from './types';
|
|
2
2
|
import { forOwn } from '../../utils/lang';
|
|
3
3
|
import { IReadinessManager } from '../../readiness/types';
|
|
4
|
-
import { ISplitApi } from '../../services/types';
|
|
5
4
|
import { IStorageSync } from '../../storages/types';
|
|
6
5
|
import { mySegmentsSyncTaskFactory } from './syncTasks/mySegmentsSyncTask';
|
|
7
6
|
import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask';
|
|
8
|
-
import { ISettings } from '../../types';
|
|
9
7
|
import { getMatching } from '../../utils/key';
|
|
10
8
|
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../../readiness/constants';
|
|
11
9
|
import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger/constants';
|
|
10
|
+
import { ISyncManagerFactoryParams } from '../types';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Expose start / stop mechanism for polling data from services.
|
|
15
14
|
* For client-side API with multiple clients.
|
|
16
15
|
*/
|
|
17
16
|
export function pollingManagerCSFactory(
|
|
18
|
-
|
|
19
|
-
storage: IStorageSync,
|
|
20
|
-
readiness: IReadinessManager,
|
|
21
|
-
settings: ISettings,
|
|
17
|
+
params: ISyncManagerFactoryParams
|
|
22
18
|
): IPollingManagerCS {
|
|
23
19
|
|
|
20
|
+
const { splitApi, storage, readiness, settings } = params;
|
|
24
21
|
const log = settings.log;
|
|
25
22
|
|
|
26
23
|
const splitsSyncTask: ISplitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings);
|
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
import { splitsSyncTaskFactory } from './syncTasks/splitsSyncTask';
|
|
2
2
|
import { segmentsSyncTaskFactory } from './syncTasks/segmentsSyncTask';
|
|
3
|
-
import { IStorageSync } from '../../storages/types';
|
|
4
|
-
import { IReadinessManager } from '../../readiness/types';
|
|
5
|
-
import { ISplitApi } from '../../services/types';
|
|
6
|
-
import { ISettings } from '../../types';
|
|
7
3
|
import { IPollingManager, ISegmentsSyncTask, ISplitsSyncTask } from './types';
|
|
8
4
|
import { thenable } from '../../utils/promise/thenable';
|
|
9
5
|
import { POLLING_START, POLLING_STOP, LOG_PREFIX_SYNC_POLLING } from '../../logger/constants';
|
|
6
|
+
import { ISyncManagerFactoryParams } from '../types';
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
9
|
* Expose start / stop mechanism for pulling data from services.
|
|
13
10
|
*/
|
|
14
11
|
export function pollingManagerSSFactory(
|
|
15
|
-
|
|
16
|
-
storage: IStorageSync,
|
|
17
|
-
readiness: IReadinessManager,
|
|
18
|
-
settings: ISettings
|
|
12
|
+
params: ISyncManagerFactoryParams
|
|
19
13
|
): IPollingManager {
|
|
20
14
|
|
|
15
|
+
const { splitApi, storage, readiness, settings } = params;
|
|
21
16
|
const log = settings.log;
|
|
22
17
|
|
|
23
18
|
const splitsSyncTask: ISplitsSyncTask = splitsSyncTaskFactory(splitApi.fetchSplitChanges, storage, readiness, settings);
|
|
@@ -21,12 +21,13 @@ export function mySegmentsSyncTaskFactory(
|
|
|
21
21
|
settings.log,
|
|
22
22
|
mySegmentsUpdaterFactory(
|
|
23
23
|
settings.log,
|
|
24
|
-
mySegmentsFetcherFactory(fetchMySegments
|
|
24
|
+
mySegmentsFetcherFactory(fetchMySegments),
|
|
25
25
|
storage.splits,
|
|
26
26
|
storage.segments,
|
|
27
27
|
readiness.segments,
|
|
28
28
|
settings.startup.requestTimeoutBeforeReady,
|
|
29
29
|
settings.startup.retriesOnFailureBeforeReady,
|
|
30
|
+
matchingKey
|
|
30
31
|
),
|
|
31
32
|
settings.scheduler.segmentsRefreshRate,
|
|
32
33
|
'mySegmentsUpdater',
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { IReadinessManager } from '../../readiness/types';
|
|
2
|
-
import { ISplitApi } from '../../services/types';
|
|
3
2
|
import { IStorageSync } from '../../storages/types';
|
|
4
|
-
import { ISettings } from '../../types';
|
|
5
3
|
import { SegmentsData } from '../streaming/SSEHandler/types';
|
|
6
4
|
import { ITask, ISyncTask } from '../types';
|
|
7
5
|
|
|
@@ -23,13 +21,3 @@ export interface IPollingManagerCS extends IPollingManager {
|
|
|
23
21
|
remove(matchingKey: string): void;
|
|
24
22
|
get(matchingKey: string): ISegmentsSyncTask | undefined
|
|
25
23
|
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Signature of polling manager factory/constructor
|
|
29
|
-
*/
|
|
30
|
-
export type IPollingManagerFactoryParams = [
|
|
31
|
-
splitApi: ISplitApi,
|
|
32
|
-
storage: IStorageSync,
|
|
33
|
-
readiness: IReadinessManager,
|
|
34
|
-
settings: ISettings,
|
|
35
|
-
]
|
|
@@ -23,6 +23,7 @@ export function mySegmentsUpdaterFactory(
|
|
|
23
23
|
segmentsEventEmitter: ISegmentsEventEmitter,
|
|
24
24
|
requestTimeoutBeforeReady: number,
|
|
25
25
|
retriesOnFailureBeforeReady: number,
|
|
26
|
+
matchingKey: string
|
|
26
27
|
): IMySegmentsUpdater {
|
|
27
28
|
|
|
28
29
|
let readyOnAlreadyExistentState = true;
|
|
@@ -69,7 +70,7 @@ export function mySegmentsUpdaterFactory(
|
|
|
69
70
|
// If segmentsData is provided, there is no need to fetch mySegments
|
|
70
71
|
new Promise((res) => { updateSegments(segmentsData); res(true); }) :
|
|
71
72
|
// If not provided, fetch mySegments
|
|
72
|
-
mySegmentsFetcher(noCache, _promiseDecorator).then(segments => {
|
|
73
|
+
mySegmentsFetcher(matchingKey, noCache, _promiseDecorator).then(segments => {
|
|
73
74
|
// Only when we have downloaded segments completely, we should not keep retrying anymore
|
|
74
75
|
startingUp = false;
|
|
75
76
|
|
|
@@ -17,8 +17,7 @@ export function authenticateFactory(fetchAuth: IFetchAuth): IAuthenticate {
|
|
|
17
17
|
* @param {string[] | undefined} userKeys set of user Keys to track MY_SEGMENTS_CHANGES. It is undefined for server-side API.
|
|
18
18
|
*/
|
|
19
19
|
return function authenticate(userKeys?: string[]): Promise<IAuthToken> {
|
|
20
|
-
|
|
21
|
-
return authPromise
|
|
20
|
+
return fetchAuth(userKeys)
|
|
22
21
|
.then(resp => resp.json())
|
|
23
22
|
.then(json => {
|
|
24
23
|
if (json.token) { // empty token when `"pushEnabled": false`
|
|
@@ -19,7 +19,7 @@ export class SegmentsUpdateWorker implements IUpdateWorker {
|
|
|
19
19
|
* @param {Object} segmentsCache segments data cache
|
|
20
20
|
* @param {Object} segmentsSyncTask task for syncing segments data
|
|
21
21
|
*/
|
|
22
|
-
constructor(
|
|
22
|
+
constructor(segmentsSyncTask: ISegmentsSyncTask, segmentsCache: ISegmentsCacheSync) {
|
|
23
23
|
this.segmentsCache = segmentsCache;
|
|
24
24
|
this.segmentsSyncTask = segmentsSyncTask;
|
|
25
25
|
this.maxChangeNumbers = {};
|