@splitsoftware/splitio-commons 1.1.0 → 1.1.1-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +3 -0
- package/cjs/sdkClient/clientAttributesDecoration.js +108 -0
- package/cjs/sdkClient/clientCS.js +9 -7
- package/cjs/sdkClient/sdkClientMethodCS.js +2 -2
- package/cjs/sdkClient/sdkClientMethodCSWithTT.js +2 -2
- package/cjs/storages/inMemory/AttributesCacheInMemory.js +70 -0
- package/cjs/utils/inputValidation/attribute.js +20 -0
- package/cjs/utils/inputValidation/attributes.js +13 -1
- package/esm/sdkClient/clientAttributesDecoration.js +104 -0
- package/esm/sdkClient/clientCS.js +9 -7
- package/esm/sdkClient/sdkClientMethodCS.js +2 -2
- package/esm/sdkClient/sdkClientMethodCSWithTT.js +2 -2
- package/esm/storages/inMemory/AttributesCacheInMemory.js +67 -0
- package/esm/utils/inputValidation/attribute.js +16 -0
- package/esm/utils/inputValidation/attributes.js +11 -0
- package/package.json +1 -1
- package/src/sdkClient/clientAttributesDecoration.ts +122 -0
- package/src/sdkClient/clientCS.ts +12 -7
- package/src/sdkClient/sdkClientMethodCS.ts +2 -0
- package/src/sdkClient/sdkClientMethodCSWithTT.ts +2 -0
- package/src/storages/inMemory/AttributesCacheInMemory.ts +73 -0
- package/src/types.ts +39 -0
- package/src/utils/inputValidation/attribute.ts +21 -0
- package/src/utils/inputValidation/attributes.ts +14 -0
- package/types/logger/browser/{debugLogger.d.ts → DebugLogger.d.ts} +0 -0
- package/types/logger/browser/{errorLogger.d.ts → ErrorLogger.d.ts} +0 -0
- package/types/logger/browser/{infoLogger.d.ts → InfoLogger.d.ts} +0 -0
- package/types/logger/browser/{warnLogger.d.ts → WarnLogger.d.ts} +0 -0
- 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/types.d.ts +39 -0
- package/types/utils/inputValidation/attribute.d.ts +2 -0
- package/types/utils/inputValidation/attributes.d.ts +1 -0
- package/src/logger/.DS_Store +0 -0
- package/types/integrations/ga/GaToSplitPlugin.d.ts +0 -3
- package/types/integrations/ga/SplitToGaPlugin.d.ts +0 -4
- package/types/logger/codes.d.ts +0 -2
- package/types/logger/codesConstants.d.ts +0 -117
- package/types/logger/codesConstantsBrowser.d.ts +0 -2
- package/types/logger/codesConstantsNode.d.ts +0 -14
- package/types/logger/codesDebug.d.ts +0 -1
- package/types/logger/codesDebugBrowser.d.ts +0 -1
- package/types/logger/codesDebugNode.d.ts +0 -1
- package/types/logger/codesError.d.ts +0 -1
- package/types/logger/codesErrorNode.d.ts +0 -1
- package/types/logger/codesInfo.d.ts +0 -1
- package/types/logger/codesWarn.d.ts +0 -1
- package/types/logger/codesWarnNode.d.ts +0 -1
- package/types/logger/debugLogger.d.ts +0 -2
- package/types/logger/errorLogger.d.ts +0 -2
- package/types/logger/infoLogger.d.ts +0 -2
- package/types/logger/messages/debugBrowser.d.ts +0 -1
- package/types/logger/messages/debugNode.d.ts +0 -1
- package/types/logger/messages/errorNode.d.ts +0 -1
- package/types/logger/messages/warnNode.d.ts +0 -1
- package/types/logger/noopLogger.d.ts +0 -2
- package/types/logger/warnLogger.d.ts +0 -2
- package/types/sdkManager/sdkManagerMethod.d.ts +0 -6
- package/types/storages/getRegisteredSegments.d.ts +0 -10
- package/types/sync/polling/syncTasks/splitsSyncTask.copy.d.ts +0 -35
- package/types/sync/polling/syncTasks/splitsSyncTask.morelikeoriginal.d.ts +0 -35
- package/types/sync/streaming/AuthClient/indexV1.d.ts +0 -12
- package/types/sync/streaming/AuthClient/indexV2.d.ts +0 -8
- package/types/sync/streaming/pushManagerCS.d.ts +0 -12
- package/types/sync/streaming/pushManagerNoUsers.d.ts +0 -13
- package/types/sync/streaming/pushManagerSS.d.ts +0 -11
- package/types/sync/syncManagerFromFile.d.ts +0 -2
- package/types/sync/syncManagerFromObject.d.ts +0 -2
- package/types/sync/syncManagerOffline.d.ts +0 -9
- package/types/utils/lang/errors.d.ts +0 -10
- package/types/utils/murmur3/commons.d.ts +0 -12
- package/types/utils/settingsValidation/buildMetadata.d.ts +0 -3
- package/types/utils/settingsValidation/localhost/index.d.ts +0 -9
- package/types/utils/settingsValidation/logger.d.ts +0 -11
package/CHANGES.txt
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
1.1.1 (XXX)
|
|
2
|
+
- Added support to SDK clients on browser to optionally bind attributes to the client, keeping these loaded within the SDK along with the user ID, for easier usage when requesting flag.
|
|
3
|
+
|
|
1
4
|
1.1.0 (January 11, 2022)
|
|
2
5
|
- Added support for the SDK to run in "consumer" and "partial consumer" modes, with a pluggable implementation of it's internal storage, enabling
|
|
3
6
|
customers to implement this caching with any storage technology of choice and connect it to the SDK instance to be used instead of its default in-memory storage.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clientAttributesDecoration = void 0;
|
|
4
|
+
var AttributesCacheInMemory_1 = require("../storages/inMemory/AttributesCacheInMemory");
|
|
5
|
+
var attributes_1 = require("../utils/inputValidation/attributes");
|
|
6
|
+
var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
7
|
+
/**
|
|
8
|
+
* Add in memory attributes storage methods and combine them with any attribute received from the getTreatment/s call
|
|
9
|
+
*/
|
|
10
|
+
function clientAttributesDecoration(log, client) {
|
|
11
|
+
var attributeStorage = new AttributesCacheInMemory_1.AttributesCacheInMemory();
|
|
12
|
+
// Keep a reference to the original methods
|
|
13
|
+
var clientGetTreatment = client.getTreatment;
|
|
14
|
+
var clientGetTreatmentWithConfig = client.getTreatmentWithConfig;
|
|
15
|
+
var clientGetTreatments = client.getTreatments;
|
|
16
|
+
var clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
|
|
17
|
+
var clientTrack = client.track;
|
|
18
|
+
function getTreatment(maybeKey, maybeSplit, maybeAttributes) {
|
|
19
|
+
return clientGetTreatment(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
20
|
+
}
|
|
21
|
+
function getTreatmentWithConfig(maybeKey, maybeSplit, maybeAttributes) {
|
|
22
|
+
return clientGetTreatmentWithConfig(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
23
|
+
}
|
|
24
|
+
function getTreatments(maybeKey, maybeSplits, maybeAttributes) {
|
|
25
|
+
return clientGetTreatments(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
26
|
+
}
|
|
27
|
+
function getTreatmentsWithConfig(maybeKey, maybeSplits, maybeAttributes) {
|
|
28
|
+
return clientGetTreatmentsWithConfig(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
29
|
+
}
|
|
30
|
+
function track(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties) {
|
|
31
|
+
return clientTrack(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties);
|
|
32
|
+
}
|
|
33
|
+
function combineAttributes(maybeAttributes) {
|
|
34
|
+
var storedAttributes = attributeStorage.getAll();
|
|
35
|
+
if (Object.keys(storedAttributes).length > 0) {
|
|
36
|
+
return objectAssign_1.objectAssign({}, storedAttributes, maybeAttributes);
|
|
37
|
+
}
|
|
38
|
+
return maybeAttributes;
|
|
39
|
+
}
|
|
40
|
+
return objectAssign_1.objectAssign(client, {
|
|
41
|
+
getTreatment: getTreatment,
|
|
42
|
+
getTreatmentWithConfig: getTreatmentWithConfig,
|
|
43
|
+
getTreatments: getTreatments,
|
|
44
|
+
getTreatmentsWithConfig: getTreatmentsWithConfig,
|
|
45
|
+
track: track,
|
|
46
|
+
/**
|
|
47
|
+
* Add an attribute to client's in memory attributes storage
|
|
48
|
+
*
|
|
49
|
+
* @param {string} attributeName Attrinute name
|
|
50
|
+
* @param {string, number, boolean, list} attributeValue Attribute value
|
|
51
|
+
* @returns {boolean} true if the attribute was stored and false otherways
|
|
52
|
+
*/
|
|
53
|
+
setAttribute: function (attributeName, attributeValue) {
|
|
54
|
+
var attribute = {};
|
|
55
|
+
attribute[attributeName] = attributeValue;
|
|
56
|
+
if (!attributes_1.validateAttributesDeep(log, attribute, 'setAttribute'))
|
|
57
|
+
return false;
|
|
58
|
+
log.debug("stored " + attributeValue + " for attribute " + attributeName);
|
|
59
|
+
return attributeStorage.setAttribute(attributeName, attributeValue);
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* Returns the attribute with the given key
|
|
63
|
+
*
|
|
64
|
+
* @param {string} attributeName Attribute name
|
|
65
|
+
* @returns {Object} Attribute with the given key
|
|
66
|
+
*/
|
|
67
|
+
getAttribute: function (attributeName) {
|
|
68
|
+
log.debug("retrieved attribute " + attributeName);
|
|
69
|
+
return attributeStorage.getAttribute(attributeName + '');
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* Add to client's in memory attributes storage the attributes in 'attributes'
|
|
73
|
+
*
|
|
74
|
+
* @param {Object} attributes Object with attributes to store
|
|
75
|
+
* @returns true if attributes were stored an false otherways
|
|
76
|
+
*/
|
|
77
|
+
setAttributes: function (attributes) {
|
|
78
|
+
if (!attributes_1.validateAttributesDeep(log, attributes, 'setAttributes'))
|
|
79
|
+
return false;
|
|
80
|
+
return attributeStorage.setAttributes(attributes);
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Return all the attributes stored in client's in memory attributes storage
|
|
84
|
+
*
|
|
85
|
+
* @returns {Object} returns all the stored attributes
|
|
86
|
+
*/
|
|
87
|
+
getAttributes: function () {
|
|
88
|
+
return attributeStorage.getAll();
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* Removes from client's in memory attributes storage the attribute with the given key
|
|
92
|
+
*
|
|
93
|
+
* @param {string} attributeName
|
|
94
|
+
* @returns {boolean} true if attribute was removed and false otherways
|
|
95
|
+
*/
|
|
96
|
+
removeAttribute: function (attributeName) {
|
|
97
|
+
log.debug("removed attribute " + attributeName);
|
|
98
|
+
return attributeStorage.removeAttribute(attributeName + '');
|
|
99
|
+
},
|
|
100
|
+
/**
|
|
101
|
+
* Remove all the stored attributes in the client's in memory attribute storage
|
|
102
|
+
*/
|
|
103
|
+
clearAttributes: function () {
|
|
104
|
+
return attributeStorage.clear();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
exports.clientAttributesDecoration = clientAttributesDecoration;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.clientCSDecorator = void 0;
|
|
4
4
|
var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
5
|
+
var clientAttributesDecoration_1 = require("./clientAttributesDecoration");
|
|
5
6
|
/**
|
|
6
7
|
* Decorator that binds a key and (optionally) a traffic type to client methods
|
|
7
8
|
*
|
|
@@ -9,15 +10,16 @@ var objectAssign_1 = require("../utils/lang/objectAssign");
|
|
|
9
10
|
* @param key validated split key
|
|
10
11
|
* @param trafficType validated traffic type
|
|
11
12
|
*/
|
|
12
|
-
function clientCSDecorator(client, key, trafficType) {
|
|
13
|
-
|
|
13
|
+
function clientCSDecorator(log, client, key, trafficType) {
|
|
14
|
+
var clientCS = clientAttributesDecoration_1.clientAttributesDecoration(log, client);
|
|
15
|
+
return objectAssign_1.objectAssign(clientCS, {
|
|
14
16
|
// In the client-side API, we bind a key to the client `getTreatment*` methods
|
|
15
|
-
getTreatment:
|
|
16
|
-
getTreatmentWithConfig:
|
|
17
|
-
getTreatments:
|
|
18
|
-
getTreatmentsWithConfig:
|
|
17
|
+
getTreatment: clientCS.getTreatment.bind(clientCS, key),
|
|
18
|
+
getTreatmentWithConfig: clientCS.getTreatmentWithConfig.bind(clientCS, key),
|
|
19
|
+
getTreatments: clientCS.getTreatments.bind(clientCS, key),
|
|
20
|
+
getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
|
|
19
21
|
// Key is bound to the `track` method. Same thing happens with trafficType but only if provided
|
|
20
|
-
track: trafficType ?
|
|
22
|
+
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key)
|
|
21
23
|
});
|
|
22
24
|
}
|
|
23
25
|
exports.clientCSDecorator = clientCSDecorator;
|
|
@@ -23,7 +23,7 @@ function sdkClientMethodCSFactory(params) {
|
|
|
23
23
|
// `false` value is used as binded key of the default client, but trafficType is ignored
|
|
24
24
|
// @TODO handle as a non-recoverable error
|
|
25
25
|
var validKey = key_1.validateKey(log, key, method);
|
|
26
|
-
var mainClientInstance = clientCS_1.clientCSDecorator(sdkClient_1.sdkClientFactory(params), // @ts-ignore
|
|
26
|
+
var mainClientInstance = clientCS_1.clientCSDecorator(log, sdkClient_1.sdkClientFactory(params), // @ts-ignore
|
|
27
27
|
validKey);
|
|
28
28
|
var parsedDefaultKey = key_2.keyParser(key);
|
|
29
29
|
var defaultInstanceId = buildInstanceId(parsedDefaultKey);
|
|
@@ -58,7 +58,7 @@ function sdkClientMethodCSFactory(params) {
|
|
|
58
58
|
var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
|
|
59
59
|
// As shared clients reuse all the storage information, we don't need to check here if we
|
|
60
60
|
// will use offline or online mode. We should stick with the original decision.
|
|
61
|
-
clientInstances[instanceId] = clientCS_1.clientCSDecorator(sdkClient_1.sdkClientFactory(objectAssign_1.objectAssign({}, params, {
|
|
61
|
+
clientInstances[instanceId] = clientCS_1.clientCSDecorator(log, sdkClient_1.sdkClientFactory(objectAssign_1.objectAssign({}, params, {
|
|
62
62
|
sdkReadinessManager: sharedSdkReadiness_1,
|
|
63
63
|
storage: sharedStorage || storage,
|
|
64
64
|
syncManager: sharedSyncManager,
|
|
@@ -29,7 +29,7 @@ function sdkClientMethodCSFactory(params) {
|
|
|
29
29
|
if (trafficType !== undefined) {
|
|
30
30
|
validTrafficType = trafficType_1.validateTrafficType(log, trafficType, method);
|
|
31
31
|
}
|
|
32
|
-
var mainClientInstance = clientCS_1.clientCSDecorator(sdkClient_1.sdkClientFactory(params), // @ts-ignore
|
|
32
|
+
var mainClientInstance = clientCS_1.clientCSDecorator(log, sdkClient_1.sdkClientFactory(params), // @ts-ignore
|
|
33
33
|
validKey, validTrafficType);
|
|
34
34
|
var parsedDefaultKey = key_2.keyParser(key);
|
|
35
35
|
var defaultInstanceId = buildInstanceId(parsedDefaultKey, trafficType);
|
|
@@ -71,7 +71,7 @@ function sdkClientMethodCSFactory(params) {
|
|
|
71
71
|
var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
|
|
72
72
|
// As shared clients reuse all the storage information, we don't need to check here if we
|
|
73
73
|
// will use offline or online mode. We should stick with the original decision.
|
|
74
|
-
clientInstances[instanceId] = clientCS_1.clientCSDecorator(sdkClient_1.sdkClientFactory(objectAssign_1.objectAssign({}, params, {
|
|
74
|
+
clientInstances[instanceId] = clientCS_1.clientCSDecorator(log, sdkClient_1.sdkClientFactory(objectAssign_1.objectAssign({}, params, {
|
|
75
75
|
sdkReadinessManager: sharedSdkReadiness_1,
|
|
76
76
|
storage: sharedStorage || storage,
|
|
77
77
|
syncManager: sharedSyncManager,
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AttributesCacheInMemory = void 0;
|
|
4
|
+
var objectAssign_1 = require("../../utils/lang/objectAssign");
|
|
5
|
+
var AttributesCacheInMemory = /** @class */ (function () {
|
|
6
|
+
function AttributesCacheInMemory() {
|
|
7
|
+
this.attributesCache = {};
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Create or update the value for the given attribute
|
|
11
|
+
*
|
|
12
|
+
* @param {string} attributeName attribute name
|
|
13
|
+
* @param {Object} attributeValue attribute value
|
|
14
|
+
* @returns {boolean} the attribute was stored
|
|
15
|
+
*/
|
|
16
|
+
AttributesCacheInMemory.prototype.setAttribute = function (attributeName, attributeValue) {
|
|
17
|
+
this.attributesCache[attributeName] = attributeValue;
|
|
18
|
+
return true;
|
|
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
|
+
AttributesCacheInMemory.prototype.getAttribute = function (attributeName) {
|
|
27
|
+
return this.attributesCache[attributeName];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Create or update all the given attributes
|
|
31
|
+
*
|
|
32
|
+
* @param {[string, Object]} attributes attributes to create or update
|
|
33
|
+
* @returns {boolean} attributes were stored
|
|
34
|
+
*/
|
|
35
|
+
AttributesCacheInMemory.prototype.setAttributes = function (attributes) {
|
|
36
|
+
this.attributesCache = objectAssign_1.objectAssign(this.attributesCache, attributes);
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Retrieve the full attributes map
|
|
41
|
+
*
|
|
42
|
+
* @returns {Map<string, Object>} stored attributes
|
|
43
|
+
*/
|
|
44
|
+
AttributesCacheInMemory.prototype.getAll = function () {
|
|
45
|
+
return this.attributesCache;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Removes a given attribute from the map
|
|
49
|
+
*
|
|
50
|
+
* @param {string} attributeName attribute to remove
|
|
51
|
+
* @returns {boolean} attribute removed
|
|
52
|
+
*/
|
|
53
|
+
AttributesCacheInMemory.prototype.removeAttribute = function (attributeName) {
|
|
54
|
+
if (Object.keys(this.attributesCache).indexOf(attributeName) >= 0) {
|
|
55
|
+
delete this.attributesCache[attributeName];
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Clears all attributes stored in the SDK
|
|
62
|
+
*
|
|
63
|
+
*/
|
|
64
|
+
AttributesCacheInMemory.prototype.clear = function () {
|
|
65
|
+
this.attributesCache = {};
|
|
66
|
+
return true;
|
|
67
|
+
};
|
|
68
|
+
return AttributesCacheInMemory;
|
|
69
|
+
}());
|
|
70
|
+
exports.AttributesCacheInMemory = AttributesCacheInMemory;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateAttribute = void 0;
|
|
4
|
+
var lang_1 = require("../../utils/lang");
|
|
5
|
+
function validateAttribute(log, attributeKey, attributeValue, method) {
|
|
6
|
+
if (!lang_1.isString(attributeKey) || attributeKey.length === 0) {
|
|
7
|
+
log.warn(method + ": you passed an invalid attribute name, attribute name must be a non-empty string.");
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
var isStringVal = lang_1.isString(attributeValue);
|
|
11
|
+
var isFiniteVal = lang_1.isFiniteNumber(attributeValue);
|
|
12
|
+
var isBoolVal = lang_1.isBoolean(attributeValue);
|
|
13
|
+
var isArrayVal = Array.isArray(attributeValue);
|
|
14
|
+
if (!(isStringVal || isFiniteVal || isBoolVal || isArrayVal)) { // If it's not of valid type.
|
|
15
|
+
log.warn(method + ": you passed an invalid attribute value for " + attributeKey + ". Acceptable types are: string, number, boolean and array of strings.");
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
exports.validateAttribute = validateAttribute;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateAttributes = void 0;
|
|
3
|
+
exports.validateAttributesDeep = exports.validateAttributes = void 0;
|
|
4
4
|
var lang_1 = require("../lang");
|
|
5
|
+
var attribute_1 = require("./attribute");
|
|
5
6
|
var constants_1 = require("../../logger/constants");
|
|
6
7
|
function validateAttributes(log, maybeAttrs, method) {
|
|
7
8
|
// Attributes are optional
|
|
@@ -11,3 +12,14 @@ function validateAttributes(log, maybeAttrs, method) {
|
|
|
11
12
|
return false;
|
|
12
13
|
}
|
|
13
14
|
exports.validateAttributes = validateAttributes;
|
|
15
|
+
function validateAttributesDeep(log, maybeAttributes, method) {
|
|
16
|
+
if (!validateAttributes(log, maybeAttributes, method))
|
|
17
|
+
return false;
|
|
18
|
+
var result = true;
|
|
19
|
+
Object.keys(maybeAttributes).forEach(function (attributeKey) {
|
|
20
|
+
if (!attribute_1.validateAttribute(log, attributeKey, maybeAttributes[attributeKey], method))
|
|
21
|
+
result = false;
|
|
22
|
+
});
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
exports.validateAttributesDeep = validateAttributesDeep;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { AttributesCacheInMemory } from '../storages/inMemory/AttributesCacheInMemory';
|
|
2
|
+
import { validateAttributesDeep } from '../utils/inputValidation/attributes';
|
|
3
|
+
import { objectAssign } from '../utils/lang/objectAssign';
|
|
4
|
+
/**
|
|
5
|
+
* Add in memory attributes storage methods and combine them with any attribute received from the getTreatment/s call
|
|
6
|
+
*/
|
|
7
|
+
export function clientAttributesDecoration(log, client) {
|
|
8
|
+
var attributeStorage = new AttributesCacheInMemory();
|
|
9
|
+
// Keep a reference to the original methods
|
|
10
|
+
var clientGetTreatment = client.getTreatment;
|
|
11
|
+
var clientGetTreatmentWithConfig = client.getTreatmentWithConfig;
|
|
12
|
+
var clientGetTreatments = client.getTreatments;
|
|
13
|
+
var clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
|
|
14
|
+
var clientTrack = client.track;
|
|
15
|
+
function getTreatment(maybeKey, maybeSplit, maybeAttributes) {
|
|
16
|
+
return clientGetTreatment(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
17
|
+
}
|
|
18
|
+
function getTreatmentWithConfig(maybeKey, maybeSplit, maybeAttributes) {
|
|
19
|
+
return clientGetTreatmentWithConfig(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
20
|
+
}
|
|
21
|
+
function getTreatments(maybeKey, maybeSplits, maybeAttributes) {
|
|
22
|
+
return clientGetTreatments(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
23
|
+
}
|
|
24
|
+
function getTreatmentsWithConfig(maybeKey, maybeSplits, maybeAttributes) {
|
|
25
|
+
return clientGetTreatmentsWithConfig(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
26
|
+
}
|
|
27
|
+
function track(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties) {
|
|
28
|
+
return clientTrack(maybeKey, maybeTT, maybeEvent, maybeEventValue, maybeProperties);
|
|
29
|
+
}
|
|
30
|
+
function combineAttributes(maybeAttributes) {
|
|
31
|
+
var storedAttributes = attributeStorage.getAll();
|
|
32
|
+
if (Object.keys(storedAttributes).length > 0) {
|
|
33
|
+
return objectAssign({}, storedAttributes, maybeAttributes);
|
|
34
|
+
}
|
|
35
|
+
return maybeAttributes;
|
|
36
|
+
}
|
|
37
|
+
return objectAssign(client, {
|
|
38
|
+
getTreatment: getTreatment,
|
|
39
|
+
getTreatmentWithConfig: getTreatmentWithConfig,
|
|
40
|
+
getTreatments: getTreatments,
|
|
41
|
+
getTreatmentsWithConfig: getTreatmentsWithConfig,
|
|
42
|
+
track: track,
|
|
43
|
+
/**
|
|
44
|
+
* Add an attribute to client's in memory attributes storage
|
|
45
|
+
*
|
|
46
|
+
* @param {string} attributeName Attrinute name
|
|
47
|
+
* @param {string, number, boolean, list} attributeValue Attribute value
|
|
48
|
+
* @returns {boolean} true if the attribute was stored and false otherways
|
|
49
|
+
*/
|
|
50
|
+
setAttribute: function (attributeName, attributeValue) {
|
|
51
|
+
var attribute = {};
|
|
52
|
+
attribute[attributeName] = attributeValue;
|
|
53
|
+
if (!validateAttributesDeep(log, attribute, 'setAttribute'))
|
|
54
|
+
return false;
|
|
55
|
+
log.debug("stored " + attributeValue + " for attribute " + attributeName);
|
|
56
|
+
return attributeStorage.setAttribute(attributeName, attributeValue);
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Returns the attribute with the given key
|
|
60
|
+
*
|
|
61
|
+
* @param {string} attributeName Attribute name
|
|
62
|
+
* @returns {Object} Attribute with the given key
|
|
63
|
+
*/
|
|
64
|
+
getAttribute: function (attributeName) {
|
|
65
|
+
log.debug("retrieved attribute " + attributeName);
|
|
66
|
+
return attributeStorage.getAttribute(attributeName + '');
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* Add to client's in memory attributes storage the attributes in 'attributes'
|
|
70
|
+
*
|
|
71
|
+
* @param {Object} attributes Object with attributes to store
|
|
72
|
+
* @returns true if attributes were stored an false otherways
|
|
73
|
+
*/
|
|
74
|
+
setAttributes: function (attributes) {
|
|
75
|
+
if (!validateAttributesDeep(log, attributes, 'setAttributes'))
|
|
76
|
+
return false;
|
|
77
|
+
return attributeStorage.setAttributes(attributes);
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* Return all the attributes stored in client's in memory attributes storage
|
|
81
|
+
*
|
|
82
|
+
* @returns {Object} returns all the stored attributes
|
|
83
|
+
*/
|
|
84
|
+
getAttributes: function () {
|
|
85
|
+
return attributeStorage.getAll();
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* Removes from client's in memory attributes storage the attribute with the given key
|
|
89
|
+
*
|
|
90
|
+
* @param {string} attributeName
|
|
91
|
+
* @returns {boolean} true if attribute was removed and false otherways
|
|
92
|
+
*/
|
|
93
|
+
removeAttribute: function (attributeName) {
|
|
94
|
+
log.debug("removed attribute " + attributeName);
|
|
95
|
+
return attributeStorage.removeAttribute(attributeName + '');
|
|
96
|
+
},
|
|
97
|
+
/**
|
|
98
|
+
* Remove all the stored attributes in the client's in memory attribute storage
|
|
99
|
+
*/
|
|
100
|
+
clearAttributes: function () {
|
|
101
|
+
return attributeStorage.clear();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
|
+
import { clientAttributesDecoration } from './clientAttributesDecoration';
|
|
2
3
|
/**
|
|
3
4
|
* Decorator that binds a key and (optionally) a traffic type to client methods
|
|
4
5
|
*
|
|
@@ -6,14 +7,15 @@ import { objectAssign } from '../utils/lang/objectAssign';
|
|
|
6
7
|
* @param key validated split key
|
|
7
8
|
* @param trafficType validated traffic type
|
|
8
9
|
*/
|
|
9
|
-
export function clientCSDecorator(client, key, trafficType) {
|
|
10
|
-
|
|
10
|
+
export function clientCSDecorator(log, client, key, trafficType) {
|
|
11
|
+
var clientCS = clientAttributesDecoration(log, client);
|
|
12
|
+
return objectAssign(clientCS, {
|
|
11
13
|
// In the client-side API, we bind a key to the client `getTreatment*` methods
|
|
12
|
-
getTreatment:
|
|
13
|
-
getTreatmentWithConfig:
|
|
14
|
-
getTreatments:
|
|
15
|
-
getTreatmentsWithConfig:
|
|
14
|
+
getTreatment: clientCS.getTreatment.bind(clientCS, key),
|
|
15
|
+
getTreatmentWithConfig: clientCS.getTreatmentWithConfig.bind(clientCS, key),
|
|
16
|
+
getTreatments: clientCS.getTreatments.bind(clientCS, key),
|
|
17
|
+
getTreatmentsWithConfig: clientCS.getTreatmentsWithConfig.bind(clientCS, key),
|
|
16
18
|
// Key is bound to the `track` method. Same thing happens with trafficType but only if provided
|
|
17
|
-
track: trafficType ?
|
|
19
|
+
track: trafficType ? clientCS.track.bind(clientCS, key, trafficType) : clientCS.track.bind(clientCS, key)
|
|
18
20
|
});
|
|
19
21
|
}
|
|
@@ -20,7 +20,7 @@ export function sdkClientMethodCSFactory(params) {
|
|
|
20
20
|
// `false` value is used as binded key of the default client, but trafficType is ignored
|
|
21
21
|
// @TODO handle as a non-recoverable error
|
|
22
22
|
var validKey = validateKey(log, key, method);
|
|
23
|
-
var mainClientInstance = clientCSDecorator(sdkClientFactory(params), // @ts-ignore
|
|
23
|
+
var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), // @ts-ignore
|
|
24
24
|
validKey);
|
|
25
25
|
var parsedDefaultKey = keyParser(key);
|
|
26
26
|
var defaultInstanceId = buildInstanceId(parsedDefaultKey);
|
|
@@ -55,7 +55,7 @@ export function sdkClientMethodCSFactory(params) {
|
|
|
55
55
|
var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
|
|
56
56
|
// As shared clients reuse all the storage information, we don't need to check here if we
|
|
57
57
|
// will use offline or online mode. We should stick with the original decision.
|
|
58
|
-
clientInstances[instanceId] = clientCSDecorator(sdkClientFactory(objectAssign({}, params, {
|
|
58
|
+
clientInstances[instanceId] = clientCSDecorator(log, sdkClientFactory(objectAssign({}, params, {
|
|
59
59
|
sdkReadinessManager: sharedSdkReadiness_1,
|
|
60
60
|
storage: sharedStorage || storage,
|
|
61
61
|
syncManager: sharedSyncManager,
|
|
@@ -26,7 +26,7 @@ export function sdkClientMethodCSFactory(params) {
|
|
|
26
26
|
if (trafficType !== undefined) {
|
|
27
27
|
validTrafficType = validateTrafficType(log, trafficType, method);
|
|
28
28
|
}
|
|
29
|
-
var mainClientInstance = clientCSDecorator(sdkClientFactory(params), // @ts-ignore
|
|
29
|
+
var mainClientInstance = clientCSDecorator(log, sdkClientFactory(params), // @ts-ignore
|
|
30
30
|
validKey, validTrafficType);
|
|
31
31
|
var parsedDefaultKey = keyParser(key);
|
|
32
32
|
var defaultInstanceId = buildInstanceId(parsedDefaultKey, trafficType);
|
|
@@ -68,7 +68,7 @@ export function sdkClientMethodCSFactory(params) {
|
|
|
68
68
|
var sharedSyncManager = syncManager && sharedStorage && syncManager.shared(matchingKey, sharedSdkReadiness_1.readinessManager, sharedStorage);
|
|
69
69
|
// As shared clients reuse all the storage information, we don't need to check here if we
|
|
70
70
|
// will use offline or online mode. We should stick with the original decision.
|
|
71
|
-
clientInstances[instanceId] = clientCSDecorator(sdkClientFactory(objectAssign({}, params, {
|
|
71
|
+
clientInstances[instanceId] = clientCSDecorator(log, sdkClientFactory(objectAssign({}, params, {
|
|
72
72
|
sdkReadinessManager: sharedSdkReadiness_1,
|
|
73
73
|
storage: sharedStorage || storage,
|
|
74
74
|
syncManager: sharedSyncManager,
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { objectAssign } from '../../utils/lang/objectAssign';
|
|
2
|
+
var AttributesCacheInMemory = /** @class */ (function () {
|
|
3
|
+
function AttributesCacheInMemory() {
|
|
4
|
+
this.attributesCache = {};
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Create or update the value for the given attribute
|
|
8
|
+
*
|
|
9
|
+
* @param {string} attributeName attribute name
|
|
10
|
+
* @param {Object} attributeValue attribute value
|
|
11
|
+
* @returns {boolean} the attribute was stored
|
|
12
|
+
*/
|
|
13
|
+
AttributesCacheInMemory.prototype.setAttribute = function (attributeName, attributeValue) {
|
|
14
|
+
this.attributesCache[attributeName] = attributeValue;
|
|
15
|
+
return true;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Retrieves the value of a given attribute
|
|
19
|
+
*
|
|
20
|
+
* @param {string} attributeName attribute name
|
|
21
|
+
* @returns {Object?} stored attribute value
|
|
22
|
+
*/
|
|
23
|
+
AttributesCacheInMemory.prototype.getAttribute = function (attributeName) {
|
|
24
|
+
return this.attributesCache[attributeName];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Create or update all the given attributes
|
|
28
|
+
*
|
|
29
|
+
* @param {[string, Object]} attributes attributes to create or update
|
|
30
|
+
* @returns {boolean} attributes were stored
|
|
31
|
+
*/
|
|
32
|
+
AttributesCacheInMemory.prototype.setAttributes = function (attributes) {
|
|
33
|
+
this.attributesCache = objectAssign(this.attributesCache, attributes);
|
|
34
|
+
return true;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Retrieve the full attributes map
|
|
38
|
+
*
|
|
39
|
+
* @returns {Map<string, Object>} stored attributes
|
|
40
|
+
*/
|
|
41
|
+
AttributesCacheInMemory.prototype.getAll = function () {
|
|
42
|
+
return this.attributesCache;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Removes a given attribute from the map
|
|
46
|
+
*
|
|
47
|
+
* @param {string} attributeName attribute to remove
|
|
48
|
+
* @returns {boolean} attribute removed
|
|
49
|
+
*/
|
|
50
|
+
AttributesCacheInMemory.prototype.removeAttribute = function (attributeName) {
|
|
51
|
+
if (Object.keys(this.attributesCache).indexOf(attributeName) >= 0) {
|
|
52
|
+
delete this.attributesCache[attributeName];
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Clears all attributes stored in the SDK
|
|
59
|
+
*
|
|
60
|
+
*/
|
|
61
|
+
AttributesCacheInMemory.prototype.clear = function () {
|
|
62
|
+
this.attributesCache = {};
|
|
63
|
+
return true;
|
|
64
|
+
};
|
|
65
|
+
return AttributesCacheInMemory;
|
|
66
|
+
}());
|
|
67
|
+
export { AttributesCacheInMemory };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { isString, isFiniteNumber, isBoolean } from '../../utils/lang';
|
|
2
|
+
export function validateAttribute(log, attributeKey, attributeValue, method) {
|
|
3
|
+
if (!isString(attributeKey) || attributeKey.length === 0) {
|
|
4
|
+
log.warn(method + ": you passed an invalid attribute name, attribute name must be a non-empty string.");
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
var isStringVal = isString(attributeValue);
|
|
8
|
+
var isFiniteVal = isFiniteNumber(attributeValue);
|
|
9
|
+
var isBoolVal = isBoolean(attributeValue);
|
|
10
|
+
var isArrayVal = Array.isArray(attributeValue);
|
|
11
|
+
if (!(isStringVal || isFiniteVal || isBoolVal || isArrayVal)) { // If it's not of valid type.
|
|
12
|
+
log.warn(method + ": you passed an invalid attribute value for " + attributeKey + ". Acceptable types are: string, number, boolean and array of strings.");
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
@@ -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
|
+
}
|