@splitsoftware/splitio 10.16.1-rc.0 → 10.17.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 +6 -2
- package/CONTRIBUTORS-GUIDE.md +13 -12
- package/es/client/attributesDecoration.js +116 -0
- package/es/client/browser.js +2 -2
- package/es/engine/evaluator/index.js +3 -4
- package/es/storage/AttributesCache/InMemory.js +84 -0
- package/es/utils/inputValidation/attribute.js +22 -0
- package/es/utils/inputValidation/attributes.js +9 -0
- package/es/utils/settings/index.js +1 -1
- package/lib/client/attributesDecoration.js +128 -0
- package/lib/client/browser.js +5 -5
- package/lib/engine/evaluator/index.js +3 -5
- package/lib/storage/AttributesCache/InMemory.js +92 -0
- package/lib/utils/inputValidation/attribute.js +32 -0
- package/lib/utils/inputValidation/attributes.js +12 -0
- package/lib/utils/settings/index.js +1 -1
- package/package.json +6 -8
- package/src/client/attributesDecoration.js +112 -0
- package/src/client/browser.js +2 -2
- package/src/engine/evaluator/index.js +3 -4
- package/src/storage/AttributesCache/InMemory.js +75 -0
- package/src/utils/inputValidation/attribute.js +22 -0
- package/src/utils/inputValidation/attributes.js +14 -0
- package/src/utils/settings/index.js +1 -1
- package/types/index.d.ts +1 -1
- package/types/splitio.d.ts +102 -27
package/CHANGES.txt
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
10.17.0 (January 3, 2022)
|
|
2
|
+
- Added Attribute binding feature to allow to optionally keep user attributes loaded within the SDK, along with the user ID, for easier usage when requesting flag.
|
|
3
|
+
- Bugfixing - Fixed an issue returning dynamic configs when the treatment name contains a dot (".").
|
|
4
|
+
|
|
1
5
|
10.16.1 (October 25, 2021)
|
|
2
|
-
- Updated some internal modules to optimize the time efficiency of split evaluations (i.e., `getTreatment(s)` method calls)
|
|
3
|
-
- Updated some dependencies for vulnerability fixes.
|
|
6
|
+
- Updated some internal modules to optimize the time efficiency of split evaluations (i.e., `getTreatment(s)` method calls).
|
|
7
|
+
- Updated some dependencies for vulnerability fixes (ioredis, @babel/cli, eslint, eslint-plugin-compat).
|
|
4
8
|
- Bugfixing - Fixed localhost mode with localStorage, to use a mock in memory instead of localStorage API directly but keep emitting emit SDK_READY_FROM_CACHE event.
|
|
5
9
|
|
|
6
10
|
10.16.0 (September 28, 2021)
|
package/CONTRIBUTORS-GUIDE.md
CHANGED
|
@@ -7,18 +7,19 @@ Split SDK is an open source project and we welcome feedback and contribution. Th
|
|
|
7
7
|
### Development process
|
|
8
8
|
|
|
9
9
|
1. Fork the repository and create a topic branch from `development` branch. Please use a descriptive name for your branch.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4.
|
|
13
|
-
5.
|
|
14
|
-
6. Run the
|
|
15
|
-
7. Run
|
|
16
|
-
8. Run
|
|
17
|
-
9.
|
|
18
|
-
10.
|
|
19
|
-
11.
|
|
20
|
-
12.
|
|
21
|
-
13.
|
|
10
|
+
2. Run `nvm use` to ensure that you are using the right npm and node version, and `npm install` to have the dependencies up to date.
|
|
11
|
+
3. While developing, use descriptive messages in your commits. Avoid short or meaningless sentences like: "fix bug".
|
|
12
|
+
4. Make sure to add tests for both positive and negative cases.
|
|
13
|
+
5. If your changes have any impact on the public API, make sure you update the TypeScript delcarations as well as it's related test file.
|
|
14
|
+
6. Run the linter script of the project and fix any issues you find.
|
|
15
|
+
7. Run the build script and make sure it runs with no errors.
|
|
16
|
+
8. Run all tests and make sure there are no failures.
|
|
17
|
+
9. Run the TypeScript declarations tests and make sure it compiles correctly.
|
|
18
|
+
10. `git push` your changes to GitHub within your topic branch.
|
|
19
|
+
11. Open a Pull Request(PR) from your forked repo and into the `development` branch of the original repository.
|
|
20
|
+
12. When creating your PR, please fill out all the fields of the PR template, as applicable, for the project.
|
|
21
|
+
13. Check for conflicts once the pull request is created to make sure your PR can be merged cleanly into `development`.
|
|
22
|
+
14. Keep an eye out for any feedback or comments from Split's SDK team.
|
|
22
23
|
|
|
23
24
|
### Building the SDK
|
|
24
25
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import ClientInputValidationLayer from './inputValidation';
|
|
2
|
+
import AttributesCacheInMemory from '../storage/AttributesCache/InMemory';
|
|
3
|
+
import { validateAttributesDeep } from '../utils/inputValidation/attributes';
|
|
4
|
+
import logFactory from '../utils/logger';
|
|
5
|
+
import objectAssign from 'object-assign';
|
|
6
|
+
var log = logFactory('splitio-client');
|
|
7
|
+
/**
|
|
8
|
+
* Add in memory attributes storage methods and combine them with any attribute received from the getTreatment/s call
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export default function ClientAttributesDecorationLayer(context, isKeyBinded, isTTBinded) {
|
|
12
|
+
var client = ClientInputValidationLayer(context, isKeyBinded, isTTBinded);
|
|
13
|
+
var attributeStorage = new AttributesCacheInMemory(); // Keep a reference to the original methods
|
|
14
|
+
|
|
15
|
+
var clientGetTreatment = client.getTreatment;
|
|
16
|
+
var clientGetTreatmentWithConfig = client.getTreatmentWithConfig;
|
|
17
|
+
var clientGetTreatments = client.getTreatments;
|
|
18
|
+
var clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Add an attribute to client's in memory attributes storage
|
|
21
|
+
*
|
|
22
|
+
* @param {string} attributeName Attrinute name
|
|
23
|
+
* @param {string, number, boolean, list} attributeValue Attribute value
|
|
24
|
+
* @returns {boolean} true if the attribute was stored and false otherways
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
client.setAttribute = function (attributeName, attributeValue) {
|
|
28
|
+
var attribute = {};
|
|
29
|
+
attribute[attributeName] = attributeValue;
|
|
30
|
+
if (!validateAttributesDeep(attribute)) return false;
|
|
31
|
+
log.debug("stored " + attributeValue + " for attribute " + attributeName);
|
|
32
|
+
return attributeStorage.setAttribute(attributeName, attributeValue);
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Returns the attribute with the given key
|
|
36
|
+
*
|
|
37
|
+
* @param {string} attributeName Attribute name
|
|
38
|
+
* @returns {Object} Attribute with the given key
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
client.getAttribute = function (attributeName) {
|
|
43
|
+
log.debug("retrieved attribute " + attributeName);
|
|
44
|
+
return attributeStorage.getAttribute(attributeName + '');
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Add to client's in memory attributes storage the attributes in 'attributes'
|
|
48
|
+
*
|
|
49
|
+
* @param {Object} attributes Object with attributes to store
|
|
50
|
+
* @returns true if attributes were stored an false otherways
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
client.setAttributes = function (attributes) {
|
|
55
|
+
if (!validateAttributesDeep(attributes)) return false;
|
|
56
|
+
return attributeStorage.setAttributes(attributes);
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Return all the attributes stored in client's in memory attributes storage
|
|
60
|
+
*
|
|
61
|
+
* @returns {Object} returns all the stored attributes
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
client.getAttributes = function () {
|
|
66
|
+
return attributeStorage.getAll();
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Removes from client's in memory attributes storage the attribute with the given key
|
|
70
|
+
*
|
|
71
|
+
* @param {string} attributeName
|
|
72
|
+
* @returns {boolean} true if attribute was removed and false otherways
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
client.removeAttribute = function (attributeName) {
|
|
77
|
+
log.debug("removed attribute " + attributeName);
|
|
78
|
+
return attributeStorage.removeAttribute(attributeName + '');
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Remove all the stored attributes in the client's in memory attribute storage
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
client.clearAttributes = function () {
|
|
86
|
+
return attributeStorage.clear();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
client.getTreatment = function (maybeKey, maybeSplit, maybeAttributes) {
|
|
90
|
+
return clientGetTreatment(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
client.getTreatmentWithConfig = function (maybeKey, maybeSplit, maybeAttributes) {
|
|
94
|
+
return clientGetTreatmentWithConfig(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
client.getTreatments = function (maybeKey, maybeSplits, maybeAttributes) {
|
|
98
|
+
return clientGetTreatments(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
client.getTreatmentsWithConfig = function (maybeKey, maybeSplits, maybeAttributes) {
|
|
102
|
+
return clientGetTreatmentsWithConfig(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
function combineAttributes(maybeAttributes) {
|
|
106
|
+
var storedAttributes = attributeStorage.getAll();
|
|
107
|
+
|
|
108
|
+
if (Object.keys(storedAttributes).length > 0) {
|
|
109
|
+
return objectAssign({}, storedAttributes, maybeAttributes);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return maybeAttributes;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return client;
|
|
116
|
+
}
|
package/es/client/browser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { get } from '../utils/lang';
|
|
2
|
-
import
|
|
2
|
+
import ClientAttributesDecorationLayer from './attributesDecoration';
|
|
3
3
|
import { LOCALHOST_MODE } from '../utils/constants';
|
|
4
4
|
import { validateKey, validateTrafficType } from '../utils/inputValidation';
|
|
5
5
|
|
|
@@ -25,7 +25,7 @@ function BrowserClientFactory(context) {
|
|
|
25
25
|
trackBindings.push(tt);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
var client =
|
|
28
|
+
var client = ClientAttributesDecorationLayer(context, true, trackBindings.length > 1);
|
|
29
29
|
client.isBrowserClient = true; // In the browser land, we can bind the key and the traffic type (if provided)
|
|
30
30
|
|
|
31
31
|
client.getTreatment = client.getTreatment.bind(client, settings.core.key);
|
|
@@ -16,7 +16,6 @@ limitations under the License.
|
|
|
16
16
|
import Engine from '../';
|
|
17
17
|
import thenable from '../../utils/promise/thenable';
|
|
18
18
|
import * as LabelsConstants from '../../utils/labels';
|
|
19
|
-
import { get } from '../../utils/lang';
|
|
20
19
|
import { CONTROL } from '../../utils/constants';
|
|
21
20
|
var treatmentException = {
|
|
22
21
|
treatment: CONTROL,
|
|
@@ -74,18 +73,18 @@ function getEvaluation(stringifiedSplit, key, attributes, storage) {
|
|
|
74
73
|
if (stringifiedSplit) {
|
|
75
74
|
var splitJSON = JSON.parse(stringifiedSplit);
|
|
76
75
|
var split = Engine.parse(splitJSON, storage);
|
|
77
|
-
evaluation = split.getTreatment(key, attributes, evaluateFeature); // If the storage is async
|
|
76
|
+
evaluation = split.getTreatment(key, attributes, evaluateFeature); // If the storage is async and the evaluated split uses segment, evaluation is thenable
|
|
78
77
|
|
|
79
78
|
if (thenable(evaluation)) {
|
|
80
79
|
return evaluation.then(function (result) {
|
|
81
80
|
result.changeNumber = split.getChangeNumber();
|
|
82
|
-
result.config =
|
|
81
|
+
result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
|
|
83
82
|
return result;
|
|
84
83
|
});
|
|
85
84
|
} else {
|
|
86
85
|
evaluation.changeNumber = split.getChangeNumber(); // Always sync and optional
|
|
87
86
|
|
|
88
|
-
evaluation.config =
|
|
87
|
+
evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import objectAssign from 'object-assign';
|
|
2
|
+
|
|
3
|
+
var AttributesCacheInMemory = /*#__PURE__*/function () {
|
|
4
|
+
function AttributesCacheInMemory() {
|
|
5
|
+
this.attributesCache = {};
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Create or update the value for the given attribute
|
|
9
|
+
*
|
|
10
|
+
* @param {string} attributeName attribute name
|
|
11
|
+
* @param {Object} attributeValue attribute value
|
|
12
|
+
* @returns {boolean} the attribute was stored
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
var _proto = AttributesCacheInMemory.prototype;
|
|
17
|
+
|
|
18
|
+
_proto.setAttribute = function setAttribute(attributeName, attributeValue) {
|
|
19
|
+
this.attributesCache[attributeName] = attributeValue;
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Retrieves the value of a given attribute
|
|
24
|
+
*
|
|
25
|
+
* @param {string} attributeName attribute name
|
|
26
|
+
* @returns {Object?} stored attribute value
|
|
27
|
+
*/
|
|
28
|
+
;
|
|
29
|
+
|
|
30
|
+
_proto.getAttribute = function getAttribute(attributeName) {
|
|
31
|
+
return this.attributesCache[attributeName];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Create or update all the given attributes
|
|
35
|
+
*
|
|
36
|
+
* @param {[string, Object]} attributes attributes to create or update
|
|
37
|
+
* @returns {boolean} attributes were stored
|
|
38
|
+
*/
|
|
39
|
+
;
|
|
40
|
+
|
|
41
|
+
_proto.setAttributes = function setAttributes(attributes) {
|
|
42
|
+
this.attributesCache = objectAssign(this.attributesCache, attributes);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Retrieve the full attributes map
|
|
47
|
+
*
|
|
48
|
+
* @returns {Map<string, Object>} stored attributes
|
|
49
|
+
*/
|
|
50
|
+
;
|
|
51
|
+
|
|
52
|
+
_proto.getAll = function getAll() {
|
|
53
|
+
return this.attributesCache;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Removes a given attribute from the map
|
|
57
|
+
*
|
|
58
|
+
* @param {string} attributeName attribute to remove
|
|
59
|
+
* @returns {boolean} attribute removed
|
|
60
|
+
*/
|
|
61
|
+
;
|
|
62
|
+
|
|
63
|
+
_proto.removeAttribute = function removeAttribute(attributeName) {
|
|
64
|
+
if (Object.keys(this.attributesCache).indexOf(attributeName) >= 0) {
|
|
65
|
+
delete this.attributesCache[attributeName];
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Clears all attributes stored in the SDK
|
|
73
|
+
*
|
|
74
|
+
*/
|
|
75
|
+
;
|
|
76
|
+
|
|
77
|
+
_proto.clear = function clear() {
|
|
78
|
+
this.attributesCache = {};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
return AttributesCacheInMemory;
|
|
82
|
+
}();
|
|
83
|
+
|
|
84
|
+
export default AttributesCacheInMemory;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isString, numberIsFinite, isBoolean } from '../lang';
|
|
2
|
+
import logFactory from '../logger';
|
|
3
|
+
var log = logFactory('');
|
|
4
|
+
export function validateAttribute(attributeKey, attributeValue, method) {
|
|
5
|
+
if (!isString(attributeKey) || attributeKey.length === 0) {
|
|
6
|
+
log.warn(method + ": you passed an invalid attribute name, attribute name must be a non-empty string.");
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
var isStringVal = isString(attributeValue);
|
|
11
|
+
var isFiniteVal = numberIsFinite(attributeValue);
|
|
12
|
+
var isBoolVal = isBoolean(attributeValue);
|
|
13
|
+
var isArrayVal = Array.isArray(attributeValue);
|
|
14
|
+
|
|
15
|
+
if (!(isStringVal || isFiniteVal || isBoolVal || isArrayVal)) {
|
|
16
|
+
// If it's not of valid type.
|
|
17
|
+
log.warn(method + ": you passed an invalid attribute value for " + attributeKey + ". Acceptable types are: string, number, boolean and array of strings.");
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isObject } from '../lang';
|
|
2
2
|
import logFactory from '../logger';
|
|
3
|
+
import { validateAttribute } from './attribute';
|
|
3
4
|
var log = logFactory('');
|
|
4
5
|
export function validateAttributes(maybeAttrs, method) {
|
|
5
6
|
// Attributes are optional
|
|
@@ -7,4 +8,12 @@ export function validateAttributes(maybeAttrs, method) {
|
|
|
7
8
|
return maybeAttrs;
|
|
8
9
|
log.error(method + ": attributes must be a plain object.");
|
|
9
10
|
return false;
|
|
11
|
+
}
|
|
12
|
+
export function validateAttributesDeep(maybeAttributes, method) {
|
|
13
|
+
if (!validateAttributes(maybeAttributes, method)) return false;
|
|
14
|
+
var result = true;
|
|
15
|
+
Object.keys(maybeAttributes).forEach(function (attributeKey) {
|
|
16
|
+
if (!validateAttribute(attributeKey, maybeAttributes[attributeKey], method)) result = false;
|
|
17
|
+
});
|
|
18
|
+
return result;
|
|
10
19
|
}
|
|
@@ -25,7 +25,7 @@ import validateSplitFilters from '../inputValidation/splitFilters';
|
|
|
25
25
|
import { API } from '../../utils/logger';
|
|
26
26
|
import { STANDALONE_MODE, STORAGE_MEMORY, CONSUMER_MODE, OPTIMIZED } from '../../utils/constants';
|
|
27
27
|
import validImpressionsMode from './impressionsMode';
|
|
28
|
-
var version = '10.
|
|
28
|
+
var version = '10.17.0';
|
|
29
29
|
var eventsEndpointMatcher = /^\/(testImpressions|metrics|events)/;
|
|
30
30
|
var authEndpointMatcher = /^\/v2\/auth/;
|
|
31
31
|
var streamingEndpointMatcher = /^\/(sse|event-stream)/;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
exports.__esModule = true;
|
|
6
|
+
exports.default = ClientAttributesDecorationLayer;
|
|
7
|
+
|
|
8
|
+
var _inputValidation = _interopRequireDefault(require("./inputValidation"));
|
|
9
|
+
|
|
10
|
+
var _InMemory = _interopRequireDefault(require("../storage/AttributesCache/InMemory"));
|
|
11
|
+
|
|
12
|
+
var _attributes = require("../utils/inputValidation/attributes");
|
|
13
|
+
|
|
14
|
+
var _logger = _interopRequireDefault(require("../utils/logger"));
|
|
15
|
+
|
|
16
|
+
var _objectAssign = _interopRequireDefault(require("object-assign"));
|
|
17
|
+
|
|
18
|
+
var log = (0, _logger.default)('splitio-client');
|
|
19
|
+
/**
|
|
20
|
+
* Add in memory attributes storage methods and combine them with any attribute received from the getTreatment/s call
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
function ClientAttributesDecorationLayer(context, isKeyBinded, isTTBinded) {
|
|
24
|
+
var client = (0, _inputValidation.default)(context, isKeyBinded, isTTBinded);
|
|
25
|
+
var attributeStorage = new _InMemory.default(); // Keep a reference to the original methods
|
|
26
|
+
|
|
27
|
+
var clientGetTreatment = client.getTreatment;
|
|
28
|
+
var clientGetTreatmentWithConfig = client.getTreatmentWithConfig;
|
|
29
|
+
var clientGetTreatments = client.getTreatments;
|
|
30
|
+
var clientGetTreatmentsWithConfig = client.getTreatmentsWithConfig;
|
|
31
|
+
/**
|
|
32
|
+
* Add an attribute to client's in memory attributes storage
|
|
33
|
+
*
|
|
34
|
+
* @param {string} attributeName Attrinute name
|
|
35
|
+
* @param {string, number, boolean, list} attributeValue Attribute value
|
|
36
|
+
* @returns {boolean} true if the attribute was stored and false otherways
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
client.setAttribute = function (attributeName, attributeValue) {
|
|
40
|
+
var attribute = {};
|
|
41
|
+
attribute[attributeName] = attributeValue;
|
|
42
|
+
if (!(0, _attributes.validateAttributesDeep)(attribute)) return false;
|
|
43
|
+
log.debug("stored " + attributeValue + " for attribute " + attributeName);
|
|
44
|
+
return attributeStorage.setAttribute(attributeName, attributeValue);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Returns the attribute with the given key
|
|
48
|
+
*
|
|
49
|
+
* @param {string} attributeName Attribute name
|
|
50
|
+
* @returns {Object} Attribute with the given key
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
client.getAttribute = function (attributeName) {
|
|
55
|
+
log.debug("retrieved attribute " + attributeName);
|
|
56
|
+
return attributeStorage.getAttribute(attributeName + '');
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Add to client's in memory attributes storage the attributes in 'attributes'
|
|
60
|
+
*
|
|
61
|
+
* @param {Object} attributes Object with attributes to store
|
|
62
|
+
* @returns true if attributes were stored an false otherways
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
client.setAttributes = function (attributes) {
|
|
67
|
+
if (!(0, _attributes.validateAttributesDeep)(attributes)) return false;
|
|
68
|
+
return attributeStorage.setAttributes(attributes);
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Return all the attributes stored in client's in memory attributes storage
|
|
72
|
+
*
|
|
73
|
+
* @returns {Object} returns all the stored attributes
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
client.getAttributes = function () {
|
|
78
|
+
return attributeStorage.getAll();
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Removes from client's in memory attributes storage the attribute with the given key
|
|
82
|
+
*
|
|
83
|
+
* @param {string} attributeName
|
|
84
|
+
* @returns {boolean} true if attribute was removed and false otherways
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
client.removeAttribute = function (attributeName) {
|
|
89
|
+
log.debug("removed attribute " + attributeName);
|
|
90
|
+
return attributeStorage.removeAttribute(attributeName + '');
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Remove all the stored attributes in the client's in memory attribute storage
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
client.clearAttributes = function () {
|
|
98
|
+
return attributeStorage.clear();
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
client.getTreatment = function (maybeKey, maybeSplit, maybeAttributes) {
|
|
102
|
+
return clientGetTreatment(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
client.getTreatmentWithConfig = function (maybeKey, maybeSplit, maybeAttributes) {
|
|
106
|
+
return clientGetTreatmentWithConfig(maybeKey, maybeSplit, combineAttributes(maybeAttributes));
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
client.getTreatments = function (maybeKey, maybeSplits, maybeAttributes) {
|
|
110
|
+
return clientGetTreatments(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
client.getTreatmentsWithConfig = function (maybeKey, maybeSplits, maybeAttributes) {
|
|
114
|
+
return clientGetTreatmentsWithConfig(maybeKey, maybeSplits, combineAttributes(maybeAttributes));
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
function combineAttributes(maybeAttributes) {
|
|
118
|
+
var storedAttributes = attributeStorage.getAll();
|
|
119
|
+
|
|
120
|
+
if (Object.keys(storedAttributes).length > 0) {
|
|
121
|
+
return (0, _objectAssign.default)({}, storedAttributes, maybeAttributes);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return maybeAttributes;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return client;
|
|
128
|
+
}
|
package/lib/client/browser.js
CHANGED
|
@@ -7,11 +7,11 @@ exports.default = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _lang = require("../utils/lang");
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _attributesDecoration = _interopRequireDefault(require("./attributesDecoration"));
|
|
11
11
|
|
|
12
12
|
var _constants = require("../utils/constants");
|
|
13
13
|
|
|
14
|
-
var
|
|
14
|
+
var _inputValidation = require("../utils/inputValidation");
|
|
15
15
|
|
|
16
16
|
function BrowserClientFactory(context) {
|
|
17
17
|
var _client$track;
|
|
@@ -23,19 +23,19 @@ function BrowserClientFactory(context) {
|
|
|
23
23
|
if (settings.mode === _constants.LOCALHOST_MODE && maybeKey === undefined) {
|
|
24
24
|
settings.core.key = 'localhost_key';
|
|
25
25
|
} else {
|
|
26
|
-
settings.core.key = (0,
|
|
26
|
+
settings.core.key = (0, _inputValidation.validateKey)(maybeKey, 'Client instantiation');
|
|
27
27
|
} // Key is also binded to the .track method. Same thing happens with trafficType but only if present on configs. (not required)
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
var trackBindings = [settings.core.key];
|
|
31
31
|
|
|
32
32
|
if (maybeTT !== undefined) {
|
|
33
|
-
var tt = (0,
|
|
33
|
+
var tt = (0, _inputValidation.validateTrafficType)(maybeTT, 'Client instantiation');
|
|
34
34
|
settings.core.trafficType = tt;
|
|
35
35
|
trackBindings.push(tt);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
var client = (0,
|
|
38
|
+
var client = (0, _attributesDecoration.default)(context, true, trackBindings.length > 1);
|
|
39
39
|
client.isBrowserClient = true; // In the browser land, we can bind the key and the traffic type (if provided)
|
|
40
40
|
|
|
41
41
|
client.getTreatment = client.getTreatment.bind(client, settings.core.key);
|
|
@@ -14,8 +14,6 @@ var _thenable = _interopRequireDefault(require("../../utils/promise/thenable"));
|
|
|
14
14
|
|
|
15
15
|
var LabelsConstants = _interopRequireWildcard(require("../../utils/labels"));
|
|
16
16
|
|
|
17
|
-
var _lang = require("../../utils/lang");
|
|
18
|
-
|
|
19
17
|
var _constants = require("../../utils/constants");
|
|
20
18
|
|
|
21
19
|
/**
|
|
@@ -93,18 +91,18 @@ function getEvaluation(stringifiedSplit, key, attributes, storage) {
|
|
|
93
91
|
|
|
94
92
|
var split = _.default.parse(splitJSON, storage);
|
|
95
93
|
|
|
96
|
-
evaluation = split.getTreatment(key, attributes, evaluateFeature); // If the storage is async
|
|
94
|
+
evaluation = split.getTreatment(key, attributes, evaluateFeature); // If the storage is async and the evaluated split uses segment, evaluation is thenable
|
|
97
95
|
|
|
98
96
|
if ((0, _thenable.default)(evaluation)) {
|
|
99
97
|
return evaluation.then(function (result) {
|
|
100
98
|
result.changeNumber = split.getChangeNumber();
|
|
101
|
-
result.config =
|
|
99
|
+
result.config = splitJSON.configurations && splitJSON.configurations[result.treatment] || null;
|
|
102
100
|
return result;
|
|
103
101
|
});
|
|
104
102
|
} else {
|
|
105
103
|
evaluation.changeNumber = split.getChangeNumber(); // Always sync and optional
|
|
106
104
|
|
|
107
|
-
evaluation.config =
|
|
105
|
+
evaluation.config = splitJSON.configurations && splitJSON.configurations[evaluation.treatment] || null;
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
108
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
exports.__esModule = true;
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _objectAssign = _interopRequireDefault(require("object-assign"));
|
|
9
|
+
|
|
10
|
+
var AttributesCacheInMemory = /*#__PURE__*/function () {
|
|
11
|
+
function AttributesCacheInMemory() {
|
|
12
|
+
this.attributesCache = {};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create or update the value for the given attribute
|
|
16
|
+
*
|
|
17
|
+
* @param {string} attributeName attribute name
|
|
18
|
+
* @param {Object} attributeValue attribute value
|
|
19
|
+
* @returns {boolean} the attribute was stored
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
var _proto = AttributesCacheInMemory.prototype;
|
|
24
|
+
|
|
25
|
+
_proto.setAttribute = function setAttribute(attributeName, attributeValue) {
|
|
26
|
+
this.attributesCache[attributeName] = attributeValue;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Retrieves the value of a given attribute
|
|
31
|
+
*
|
|
32
|
+
* @param {string} attributeName attribute name
|
|
33
|
+
* @returns {Object?} stored attribute value
|
|
34
|
+
*/
|
|
35
|
+
;
|
|
36
|
+
|
|
37
|
+
_proto.getAttribute = function getAttribute(attributeName) {
|
|
38
|
+
return this.attributesCache[attributeName];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create or update all the given attributes
|
|
42
|
+
*
|
|
43
|
+
* @param {[string, Object]} attributes attributes to create or update
|
|
44
|
+
* @returns {boolean} attributes were stored
|
|
45
|
+
*/
|
|
46
|
+
;
|
|
47
|
+
|
|
48
|
+
_proto.setAttributes = function setAttributes(attributes) {
|
|
49
|
+
this.attributesCache = (0, _objectAssign.default)(this.attributesCache, attributes);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Retrieve the full attributes map
|
|
54
|
+
*
|
|
55
|
+
* @returns {Map<string, Object>} stored attributes
|
|
56
|
+
*/
|
|
57
|
+
;
|
|
58
|
+
|
|
59
|
+
_proto.getAll = function getAll() {
|
|
60
|
+
return this.attributesCache;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Removes a given attribute from the map
|
|
64
|
+
*
|
|
65
|
+
* @param {string} attributeName attribute to remove
|
|
66
|
+
* @returns {boolean} attribute removed
|
|
67
|
+
*/
|
|
68
|
+
;
|
|
69
|
+
|
|
70
|
+
_proto.removeAttribute = function removeAttribute(attributeName) {
|
|
71
|
+
if (Object.keys(this.attributesCache).indexOf(attributeName) >= 0) {
|
|
72
|
+
delete this.attributesCache[attributeName];
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Clears all attributes stored in the SDK
|
|
80
|
+
*
|
|
81
|
+
*/
|
|
82
|
+
;
|
|
83
|
+
|
|
84
|
+
_proto.clear = function clear() {
|
|
85
|
+
this.attributesCache = {};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return AttributesCacheInMemory;
|
|
89
|
+
}();
|
|
90
|
+
|
|
91
|
+
var _default = AttributesCacheInMemory;
|
|
92
|
+
exports.default = _default;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
exports.__esModule = true;
|
|
6
|
+
exports.validateAttribute = validateAttribute;
|
|
7
|
+
|
|
8
|
+
var _lang = require("../lang");
|
|
9
|
+
|
|
10
|
+
var _logger = _interopRequireDefault(require("../logger"));
|
|
11
|
+
|
|
12
|
+
var log = (0, _logger.default)('');
|
|
13
|
+
|
|
14
|
+
function validateAttribute(attributeKey, attributeValue, method) {
|
|
15
|
+
if (!(0, _lang.isString)(attributeKey) || attributeKey.length === 0) {
|
|
16
|
+
log.warn(method + ": you passed an invalid attribute name, attribute name must be a non-empty string.");
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
var isStringVal = (0, _lang.isString)(attributeValue);
|
|
21
|
+
var isFiniteVal = (0, _lang.numberIsFinite)(attributeValue);
|
|
22
|
+
var isBoolVal = (0, _lang.isBoolean)(attributeValue);
|
|
23
|
+
var isArrayVal = Array.isArray(attributeValue);
|
|
24
|
+
|
|
25
|
+
if (!(isStringVal || isFiniteVal || isBoolVal || isArrayVal)) {
|
|
26
|
+
// If it's not of valid type.
|
|
27
|
+
log.warn(method + ": you passed an invalid attribute value for " + attributeKey + ". Acceptable types are: string, number, boolean and array of strings.");
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return true;
|
|
32
|
+
}
|