diff --git a/package-lock.json b/package-lock.json
index 6b9ac957f..651876012 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1267,11 +1267,10 @@
"dev": true
},
"@splitsoftware/splitio-commons": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.0.0.tgz",
- "integrity": "sha512-hup5YVuf9eEd9FJix7siGhFgsUJndCfeMwTLNeO+mPL2gehjGZLS0GjOMofVAa+Y1cDrpo41CQ54w/QIbixYQg==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@splitsoftware/splitio-commons/-/splitio-commons-1.2.0.tgz",
+ "integrity": "sha512-UzfB/QNjbLkjSsxsOqb6S7qEp4DXMcVZ5gD1v6NKDuuSTF+m3B/xZPp9cXrzGDgZ9pwCgDAbScWRHvRBwLfA4g==",
"requires": {
- "object-assign": "^4.1.1",
"tslib": "^2.1.0"
}
},
diff --git a/package.json b/package.json
index 630da1423..2bb18cdea 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
},
"dependencies": {
"@babel/runtime": "^7.13.10",
- "@splitsoftware/splitio-commons": "1.0.0",
+ "@splitsoftware/splitio-commons": "^1.2.0",
"@types/google.analytics": "0.0.40",
"events": "3.1.0",
"ioredis": "^4.28.0",
@@ -103,15 +103,15 @@
"test-browser-errors": "cross-env NODE_ENV=test karma start karma/errors.ci.karma.conf.js",
"test-browser-gaintegration": "cross-env NODE_ENV=test karma start karma/gaintegration.ci.karma.conf.js",
"test-browser-push": "cross-env NODE_ENV=test karma start karma/push.ci.karma.conf.js",
- "test-node": "npm run test-node-unit && npm run test-node-online && npm run test-node-offline && npm run test-node-destroy && npm run test-node-errors && npm run test-node-push && npm run test-node-redis $$ npm run test-node-custom",
+ "test-node": "npm run test-node-unit && npm run test-node-online && npm run test-node-offline && npm run test-node-destroy && npm run test-node-errors && npm run test-node-push && npm run test-node-consumer-redis $$ npm run test-node-consumer-pluggable",
"test-node-unit": "cross-env NODE_ENV=test tape -r @babel/register \"src/*/**/__tests__/**/!(browser).spec.js\" | tap-min",
"test-node-online": "cross-env NODE_ENV=test tape -r @babel/register src/__tests__/node.spec.js | tap-min",
"test-node-destroy": "cross-env NODE_ENV=test tape -r @babel/register src/__tests__/destroy/node.spec.js | tap-min",
"test-node-errors": "cross-env NODE_ENV=test tape -r @babel/register src/__tests__/errorCatching/node.spec.js | tap-min",
"test-node-offline": "cross-env NODE_ENV=test tape -r @babel/register src/__tests__/offline/node.spec.js | tap-min",
"test-node-push": "cross-env NODE_ENV=test tape -r @babel/register src/__tests__/push/node.spec.js | tap-min",
- "test-node-redis": "cross-env NODE_ENV=test tape -r @babel/register \"src/__tests__/consumerMode/node_redis.spec.js\" | tap-min",
- "test-node-custom": "cross-env NODE_ENV=test tape -r @babel/register \"src/__tests__/consumerMode/node_custom.spec.js\" | tap-min",
+ "test-node-consumer-redis": "cross-env NODE_ENV=test tape -r @babel/register \"src/__tests__/consumerMode/node_redis.spec.js\" | tap-min",
+ "test-node-consumer-pluggable": "cross-env NODE_ENV=test tape -r @babel/register \"src/__tests__/consumerMode/node_pluggable.spec.js\" | tap-min",
"pretest-ts-decls": "npm run build-es && npm run build-cjs && npm link",
"test-ts-decls": "./scripts/ts-tests.sh",
"posttest-ts-decls": "npm unlink && npm install",
diff --git a/src/__tests__/browserSuites/ready-promise.spec.js b/src/__tests__/browserSuites/ready-promise.spec.js
index c8f29c830..6a743b6d3 100644
--- a/src/__tests__/browserSuites/ready-promise.spec.js
+++ b/src/__tests__/browserSuites/ready-promise.spec.js
@@ -418,7 +418,7 @@ export default function readyPromiseAssertions(fetchMock, assert) {
// We also use the manager to get some of the promises
const manager = splitio.manager();
- // promise1 is handled inmediately. Thus, the 'reject' callback is expected to be called in 0.15 seconds aprox.
+ // promise1 is handled immediately. Thus, the 'reject' callback is expected to be called in 0.15 seconds aprox.
setTimeout(() => {
const promise1 = client.ready();
const tStart = Date.now();
@@ -434,7 +434,7 @@ export default function readyPromiseAssertions(fetchMock, assert) {
});
}, 0);
- // promise2 is handled in 0.15 seconds, when the promise is just rejected. Thus, the 'reject' callback is expected to be called inmediately (0 seconds aprox).
+ // promise2 is handled in 0.15 seconds, when the promise is just rejected. Thus, the 'reject' callback is expected to be called immediately (0 seconds aprox).
setTimeout(() => {
const promise2 = manager.ready();
const tStart = Date.now();
@@ -446,11 +446,11 @@ export default function readyPromiseAssertions(fetchMock, assert) {
t.pass('### SDK TIMED OUT - time out is triggered before retry attempt finishes');
assertGetTreatmentControlNotReady(t, client);
const tDelta = Date.now() - tStart;
- assert.ok(tDelta < 20, 'The "reject" callback is expected to be called inmediately (0 seconds aprox).');
+ assert.ok(tDelta < 20, 'The "reject" callback is expected to be called immediately (0 seconds aprox).');
});
}, fromSecondsToMillis(0.15));
- // promise3 is handled in 0.2 seconds, when the promise is just resolved. Thus, the 'resolve' callback is expected to be called inmediately (0 seconds aprox).
+ // promise3 is handled in 0.2 seconds, when the promise is just resolved. Thus, the 'resolve' callback is expected to be called immediately (0 seconds aprox).
setTimeout(() => {
const promise3 = manager.ready();
const tStart = Date.now();
@@ -459,7 +459,7 @@ export default function readyPromiseAssertions(fetchMock, assert) {
t.pass('### SDK IS READY - retry attempt finishes before the requestTimeoutBeforeReady limit');
assertGetTreatmentWhenReady(t, client);
const tDelta = Date.now() - tStart;
- assert.ok(tDelta < 20, 'The "resolve" callback is expected to be called inmediately (0 seconds aprox).');
+ assert.ok(tDelta < 20, 'The "resolve" callback is expected to be called immediately (0 seconds aprox).');
return Promise.resolve();
}, () => {
diff --git a/src/__tests__/consumerMode/ioredisWrapper.js b/src/__tests__/consumerMode/ioredisWrapper.js
index 21bd84cbd..9ea4a1787 100644
--- a/src/__tests__/consumerMode/ioredisWrapper.js
+++ b/src/__tests__/consumerMode/ioredisWrapper.js
@@ -7,12 +7,21 @@ import { parseRedisOptions } from '../../utils/settings/storage/node';
* Operations fail until `connect` is resolved when the Redis 'ready' event is emitted.
*
* @param {Object} redisOptions redis options with the format expected at `settings.storage.options`
- * @returns {import("@splitsoftware/splitio-commons/types/storages/types").ICustomStorageWrapper} wrapper for IORedis client
+ * @returns {import("@splitsoftware/splitio-commons/types/storages/types").IPluggableStorageWrapper} wrapper for IORedis client
*/
export function ioredisWrapper(redisOptions) {
+ const options = RedisAdapter._defineOptions(parseRedisOptions(redisOptions));
+
/** @type ioredis.Redis */
- let redis;
+ const redis = new ioredis(...RedisAdapter._defineLibrarySettings(options));
+
+ let isConnected = false;
+ redis.on('ready', () => { isConnected = true; });
+ // There is no need to listen for redis 'error' event, because in that case ioredis calls will be rejected.
+ // It is done to avoid getting the message `Unhandled error event: Error: connect ECONNREFUSED`.
+ // Also, we cannot reject the connect promise on an error, because the SDK will not get ready if the connection is established after the error.
+ redis.on('error', () => { });
return {
get(key) {
@@ -31,17 +40,14 @@ export function ioredisWrapper(redisOptions) {
getKeysByPrefix(prefix) {
return redis.keys(`${prefix}*`);
},
- getByPrefix(prefix) {
- return this.getKeysByPrefix(prefix).then(keys => redis.mget(...keys));
- },
getMany(keys) {
return redis.mget(...keys);
},
- incr(key) {
- return redis.incr(key);
+ incr(key, increment = 1) {
+ return redis.incrby(key, increment);
},
- decr(key) {
- return redis.decr(key);
+ decr(key, decrement = 1) {
+ return redis.decrby(key, decrement);
},
pushItems(key, items) {
return redis.rpush(key, items);
@@ -65,20 +71,13 @@ export function ioredisWrapper(redisOptions) {
return redis.smembers(key);
},
connect() {
- const options = RedisAdapter._defineOptions(parseRedisOptions(redisOptions));
- redis = new ioredis(...RedisAdapter._defineLibrarySettings(options));
-
return new Promise((res) => {
- redis.on('ready', res);
-
- // There is no need to listen for redis 'error' event, because in that case ioredis calls will be rejected.
- // But it is done to avoid getting the ioredis message `Unhandled error event: Error: connect ECONNREFUSED`.
- // If we reject the promise, the SDK client will not get ready if the redis connection is established after an error.
- redis.on('error', () => { });
+ if (isConnected) res();
+ else redis.on('ready', res);
});
},
- close() {
- return Promise.resolve(redis && redis.disconnect()); // close the connection
+ disconnect() {
+ return Promise.resolve(redis && redis.disconnect());
}
};
}
diff --git a/src/__tests__/consumerMode/node_custom.spec.js b/src/__tests__/consumerMode/node_pluggable.spec.js
similarity index 84%
rename from src/__tests__/consumerMode/node_custom.spec.js
rename to src/__tests__/consumerMode/node_pluggable.spec.js
index 49afe0631..5e943adf9 100644
--- a/src/__tests__/consumerMode/node_custom.spec.js
+++ b/src/__tests__/consumerMode/node_pluggable.spec.js
@@ -10,7 +10,6 @@ import { ioredisWrapper } from './ioredisWrapper';
import { exec } from 'child_process';
import { SplitFactory } from '../../index';
import { merge } from '../../utils/lang';
-import SettingsFactory from '../../utils/settings';
import { nearlyEqual } from '../testUtils';
const expectedSplitName = 'hierarchical_splits_testing_on';
@@ -30,15 +29,11 @@ const redisOptions = {
/** @type SplitIO.INodeAsyncSettings */
const config = {
core: {
- authorizationKey: 'uoj4sb69bjv7d4d027f7ukkitd53ek6a9ai9'
- },
- urls: {
- sdk: 'https://sdk-aws-staging.split.io/api',
- events: 'https://events-aws-staging.split.io/api'
+ authorizationKey: 'SOME API KEY' // in consumer mode, api key is only used to identify the sdk instance
},
mode: 'consumer',
storage: {
- type: 'CUSTOM',
+ type: 'PLUGGABLE',
prefix: redisPrefix,
wrapper: ioredisWrapper(redisOptions)
},
@@ -73,7 +68,7 @@ const initializeRedisServer = () => {
return promise;
};
-tape('NodeJS Custom Storage using a wrapper for Ioredis', function (t) {
+tape('NodeJS Pluggable Storage using a wrapper for Ioredis', function (t) {
t.test('Regular usage', assert => {
initializeRedisServer()
@@ -100,11 +95,16 @@ tape('NodeJS Custom Storage using a wrapper for Ioredis', function (t) {
const splitsResult = manager.splits();
assert.equal(typeof getTreatmentResult.then, 'function', 'GetTreatment calls should always return a promise on Consumer mode.');
- // NOTE: unlike others, JS SDK attempts to retrieve splits from storage even if SDK_READY has not been emitted.
- // This could change in a coming breaking change, in order to return a control treatment without calling the storage wrapper.
- // ATM, we have to set `enableOfflineQueue: false` in our ioredisWrapper, to make wrapper calls fail immediately if Redis 'ready' event was not emitted.
- // The label might be 'exception' instead of 'not ready', if the wrapper operation promise is settled after the SDK is ready.
- assert.equal(await getTreatmentResult, 'control', 'Evaluations using custom storage should be control if initiated before SDK_READY.');
+ // NOTE: unlike spec, JS SDK attempts to retrieve splits from storage even if SDK_READY has not been emitted, because it is flagged ready from cache.
+ // This could change to return control treatment without calling the storage wrapper (Breaking change).
+ // ATM, ioredisWrapper sets `enableOfflineQueue` to false (https://github.com/luin/ioredis#offline-queue), to make wrapper calls fail immediately until Redis 'ready' event is emitted.
+ // Therefore, the impression label is 'exception' instead of 'not ready'.
+ assert.equal(await getTreatmentResult, 'control', 'Evaluations using pluggable storage should be control if initiated before SDK_READY.');
+ // @TODO SDK should not be operational, until wrapper connects
+ assert.deepEqual({
+ isReadyFromCache: client.__context.get(client.__context.constants.READY_FROM_CACHE, true),
+ isReady: client.__context.get(client.__context.constants.READY, true)
+ }, { isReadyFromCache: true, isReady: undefined }, 'SDK in consumer mode is operational inmediatelly');
assert.equal(typeof trackResult.then, 'function', 'Track calls should always return a promise on Consumer mode.');
assert.false(await trackResult, 'If the event failed to be queued due to a wrapper operation failure, the promise will resolve to false');
@@ -118,43 +118,43 @@ tape('NodeJS Custom Storage using a wrapper for Ioredis', function (t) {
await client.ready();
- assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', '`getTreatment` evaluation using custom storage should be correct.');
- assert.equal((await client.getTreatmentWithConfig('other', 'UT_IN_SEGMENT')).treatment, 'off', '`getTreatmentWithConfig` evaluation using custom storage should be correct.');
+ assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', '`getTreatment` evaluation using pluggable storage should be correct.');
+ assert.equal((await client.getTreatmentWithConfig('other', 'UT_IN_SEGMENT')).treatment, 'off', '`getTreatmentWithConfig` evaluation using pluggable storage should be correct.');
- assert.equal((await client.getTreatments('UT_Segment_member', ['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'], 'off', '`getTreatments` evaluation using custom storage should be correct.');
- assert.equal((await client.getTreatmentsWithConfig('other', ['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'].treatment, 'on', '`getTreatmentsWithConfig` evaluation using custom storage should be correct.');
+ assert.equal((await client.getTreatments('UT_Segment_member', ['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'], 'off', '`getTreatments` evaluation using pluggable storage should be correct.');
+ assert.equal((await client.getTreatmentsWithConfig('other', ['UT_NOT_IN_SEGMENT']))['UT_NOT_IN_SEGMENT'].treatment, 'on', '`getTreatmentsWithConfig` evaluation using pluggable storage should be correct.');
assert.equal(await client.getTreatment('UT_Segment_member', 'UT_SET_MATCHER', {
permissions: ['admin']
- }), 'on', 'Evaluations using custom storage should be correct.');
+ }), 'on', 'Evaluations using pluggable storage should be correct.');
assert.equal(await client.getTreatment('UT_Segment_member', 'UT_SET_MATCHER', {
permissions: ['not_matching']
- }), 'off', 'Evaluations using custom storage should be correct.');
+ }), 'off', 'Evaluations using pluggable storage should be correct.');
assert.equal(await client.getTreatment('UT_Segment_member', 'UT_NOT_SET_MATCHER', {
permissions: ['create']
- }), 'off', 'Evaluations using custom storage should be correct.');
+ }), 'off', 'Evaluations using pluggable storage should be correct.');
assert.equal(await client.getTreatment('UT_Segment_member', 'UT_NOT_SET_MATCHER', {
permissions: ['not_matching']
- }), 'on', 'Evaluations using custom storage should be correct.');
+ }), 'on', 'Evaluations using pluggable storage should be correct.');
assert.deepEqual(await client.getTreatmentWithConfig('UT_Segment_member', 'UT_NOT_SET_MATCHER', {
permissions: ['not_matching']
}), {
treatment: 'on',
config: null
- }, 'Evaluations using custom storage should be correct, including configs.');
+ }, 'Evaluations using pluggable storage should be correct, including configs.');
assert.deepEqual(await client.getTreatmentWithConfig('UT_Segment_member', 'always-on-with-config'), {
treatment: 'on',
config: expectedConfig
- }, 'Evaluations using custom storage should be correct, including configs.');
+ }, 'Evaluations using pluggable storage should be correct, including configs.');
- assert.equal(await client.getTreatment('UT_Segment_member', 'always-on'), 'on', 'Evaluations using custom storage should be correct.');
+ assert.equal(await client.getTreatment('UT_Segment_member', 'always-on'), 'on', 'Evaluations using pluggable storage should be correct.');
// Below splits were added manually to the redis_mock.json file.
// They are all_keys (always evaluate to on) which depend from always-on split. the _on/off is what treatment they are expecting there.
- assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on'), 'on', 'Evaluations using custom storage should be correct.');
- assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_off'), 'off', 'Evaluations using custom storage should be correct.');
- assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using custom storage should be correct.');
+ assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on'), 'on', 'Evaluations using pluggable storage should be correct.');
+ assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_off'), 'off', 'Evaluations using pluggable storage should be correct.');
+ assert.equal(await client.getTreatment('UT_Segment_member', 'hierarchical_splits_testing_on_negated'), 'off', 'Evaluations using pluggable storage should be correct.');
assert.equal(typeof client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then, 'function', 'Track calls should always return a promise on Consumer mode.');
assert.equal(typeof client.track().then, 'function', 'Track calls should always return a promise on Consumer mode, even when parameters are incorrect.');
@@ -198,7 +198,7 @@ tape('NodeJS Custom Storage using a wrapper for Ioredis', function (t) {
// Unlike RedisAdapter, operations to ioredisWrapper fail if it is not ready.
client.getTreatment('UT_Segment_member', 'always-on').then(treatment => {
- assert.equal(treatment, 'control', 'Evaluations using custom storage should be control if Redis was not ready');
+ assert.equal(treatment, 'control', 'Evaluations using pluggable storage should be control if Redis was not ready');
});
client.track('nicolas@split.io', 'user', 'test.redis.event', 18).then(result => {
assert.false(result, 'If the event was not queued because Redis was not ready, the promise will resolve to false');
@@ -237,8 +237,8 @@ tape('NodeJS Custom Storage using a wrapper for Ioredis', function (t) {
assert.pass('Ready promise is resolved once SDK_READY is emitted');
// some asserts to test regular usage
- assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using custom storage should be correct.');
- assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using custom storage should be correct.');
+ assert.equal(await client.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using pluggable storage should be correct.');
+ assert.equal(await client.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using pluggable storage should be correct.');
assert.true(await client.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true');
assert.false(await client.track(), 'If the event was NOT succesfully queued the promise will resolve to false');
@@ -265,8 +265,8 @@ tape('NodeJS Custom Storage using a wrapper for Ioredis', function (t) {
assert.pass('SDK_READY event must be emitted');
// some asserts to test regular usage
- assert.equal(await client2.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using custom storage should be correct.');
- assert.equal(await client2.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using custom storage should be correct.');
+ assert.equal(await client2.getTreatment('UT_Segment_member', 'UT_IN_SEGMENT'), 'on', 'Evaluations using pluggable storage should be correct.');
+ assert.equal(await client2.getTreatment('other', 'UT_IN_SEGMENT'), 'off', 'Evaluations using pluggable storage should be correct.');
assert.true(await client2.track('nicolas@split.io', 'user', 'test.redis.event', 18), 'If the event was succesfully queued the promise will resolve to true');
assert.false(await client2.track(), 'If the event was NOT succesfully queued the promise will resolve to false');
@@ -384,7 +384,6 @@ tape('NodeJS Custom Storage using a wrapper for Ioredis', function (t) {
for (let config of configs) {
// Redis client and keys required to check Redis store.
- const setting = SettingsFactory({ ...config, storage: { ...config.storage, wrapper: ioredisWrapper(redisOptions) } });
const connection = new RedisClient(redisOptions.url);
const eventKey = `${redisPrefix}.SPLITIO.impressions`;
@@ -409,15 +408,15 @@ tape('NodeJS Custom Storage using a wrapper for Ioredis', function (t) {
let redisImpressions = await connection.lrange(impressionsKey, 0, -1);
assert.equal(redisImpressions.length, 1, 'After getting a treatment, we should have one impression on Redis.');
const parsedImpression = JSON.parse(redisImpressions[0]);
- assert.equal(parsedImpression.m.i, setting.core.IPAddressesEnabled ? IP_VALUE : NA, `If IPAddressesEnabled is true, the property .m.i of the impression object must be equal to the machine ip, or "${NA}" otherwise.`);
- assert.equal(parsedImpression.m.n, setting.core.IPAddressesEnabled ? HOSTNAME_VALUE : NA, `If IPAddressesEnabled is true, the property .m.n of the impression object must be equal to the machine hostname, or "${NA}" otherwise.`);
+ assert.equal(parsedImpression.m.i, sdk.settings.core.IPAddressesEnabled ? IP_VALUE : NA, `If IPAddressesEnabled is true, the property .m.i of the impression object must be equal to the machine ip, or "${NA}" otherwise.`);
+ assert.equal(parsedImpression.m.n, sdk.settings.core.IPAddressesEnabled ? HOSTNAME_VALUE : NA, `If IPAddressesEnabled is true, the property .m.n of the impression object must be equal to the machine hostname, or "${NA}" otherwise.`);
// Assert if the event object was stored properly
let redisEvents = await connection.lrange(eventKey, 0, -1);
assert.equal(redisEvents.length, 1, 'After tracking an event, we should have one event on Redis.');
const parsedEvent = JSON.parse(redisEvents[0]);
- assert.equal(parsedEvent.m.i, setting.core.IPAddressesEnabled ? IP_VALUE : NA, `If IPAddressesEnabled is true, the property .m.i of the event object must be equal to the machine ip, or "${NA}" otherwise.`);
- assert.equal(parsedEvent.m.n, setting.core.IPAddressesEnabled ? HOSTNAME_VALUE : NA, `If IPAddressesEnabled is true, the property .m.n of the event object must be equal to the machine hostname, or "${NA}" otherwise.`);
+ assert.equal(parsedEvent.m.i, sdk.settings.core.IPAddressesEnabled ? IP_VALUE : NA, `If IPAddressesEnabled is true, the property .m.i of the event object must be equal to the machine ip, or "${NA}" otherwise.`);
+ assert.equal(parsedEvent.m.n, sdk.settings.core.IPAddressesEnabled ? HOSTNAME_VALUE : NA, `If IPAddressesEnabled is true, the property .m.n of the event object must be equal to the machine hostname, or "${NA}" otherwise.`);
// Deallocate Split and Redis clients
await client.destroy();
diff --git a/src/__tests__/consumerMode/node_redis.spec.js b/src/__tests__/consumerMode/node_redis.spec.js
index 0cd01deb6..7626b2d2b 100644
--- a/src/__tests__/consumerMode/node_redis.spec.js
+++ b/src/__tests__/consumerMode/node_redis.spec.js
@@ -21,11 +21,7 @@ const redisPort = '6385';
const config = {
core: {
- authorizationKey: 'uoj4sb69bjv7d4d027f7ukkitd53ek6a9ai9'
- },
- urls: {
- sdk: 'https://sdk-aws-staging.split.io/api',
- events: 'https://events-aws-staging.split.io/api'
+ authorizationKey: 'SOME API KEY' // in consumer mode, api key is only used to identify the sdk instance
},
mode: 'consumer',
storage: {
diff --git a/src/__tests__/nodeSuites/ready-promise.spec.js b/src/__tests__/nodeSuites/ready-promise.spec.js
index 5b745803b..26d76a88d 100644
--- a/src/__tests__/nodeSuites/ready-promise.spec.js
+++ b/src/__tests__/nodeSuites/ready-promise.spec.js
@@ -389,7 +389,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) {
// We also use the manager to get some of the promises
const manager = splitio.manager();
- // promise1 is handled inmediately. Thus, the 'reject' callback is expected to be called in 0.15 seconds aprox.
+ // promise1 is handled immediately. Thus, the 'reject' callback is expected to be called in 0.15 seconds aprox.
setTimeout(() => {
const promise1 = client.ready();
const tStart = Date.now();
@@ -405,7 +405,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) {
});
}, 0);
- // promise2 is handled in 0.15 seconds, when the promise is just rejected. Thus, the 'reject' callback is expected to be called inmediately (0 seconds aprox).
+ // promise2 is handled in 0.15 seconds, when the promise is just rejected. Thus, the 'reject' callback is expected to be called immediately (0 seconds aprox).
setTimeout(() => {
const promise2 = manager.ready();
const tStart = Date.now();
@@ -417,11 +417,11 @@ export default function readyPromiseAssertions(key, fetchMock, assert) {
t.pass('### SDK TIMED OUT - time out is triggered before retry attempt finishes');
assertGetTreatmentControlNotReady(t, client, key);
const tDelta = Date.now() - tStart;
- assert.ok(tDelta < 20, 'The "reject" callback is expected to be called inmediately (0 seconds aprox).');
+ assert.ok(tDelta < 20, 'The "reject" callback is expected to be called immediately (0 seconds aprox).');
});
}, fromSecondsToMillis(0.15));
- // promise3 is handled in 0.2 seconds, when the promise is just resolved. Thus, the 'resolve' callback is expected to be called inmediately (0 seconds aprox).
+ // promise3 is handled in 0.2 seconds, when the promise is just resolved. Thus, the 'resolve' callback is expected to be called immediately (0 seconds aprox).
setTimeout(() => {
const promise3 = manager.ready();
const tStart = Date.now();
@@ -430,7 +430,7 @@ export default function readyPromiseAssertions(key, fetchMock, assert) {
t.pass('### SDK IS READY - retry attempt finishes before the requestTimeoutBeforeReady limit');
assertGetTreatmentWhenReady(t, client, key);
const tDelta = Date.now() - tStart;
- assert.ok(tDelta < 20, 'The "resolve" callback is expected to be called inmediately (0 seconds aprox).');
+ assert.ok(tDelta < 20, 'The "resolve" callback is expected to be called immediately (0 seconds aprox).');
return Promise.resolve();
}, () => {
diff --git a/src/client/inputValidation.js b/src/client/inputValidation.js
index 86d19bd77..021c2e26a 100644
--- a/src/client/inputValidation.js
+++ b/src/client/inputValidation.js
@@ -13,7 +13,7 @@ import {
validateIfReady
} from '../utils/inputValidation';
import { startsWith } from '../utils/lang';
-import { STORAGE_REDIS, STORAGE_CUSTOM, CONTROL, CONTROL_WITH_CONFIG } from '../utils/constants';
+import { STORAGE_REDIS, STORAGE_PLUGGABLE, CONTROL, CONTROL_WITH_CONFIG } from '../utils/constants';
/**
* We will validate the input before actually executing the client methods. We should "guard" the client here,
@@ -21,7 +21,7 @@ import { STORAGE_REDIS, STORAGE_CUSTOM, CONTROL, CONTROL_WITH_CONFIG } from '../
*/
function ClientInputValidationLayer(context, isKeyBinded, isTTBinded) {
const settings = context.get(context.constants.SETTINGS);
- const isStorageSync = settings.storage.type !== STORAGE_REDIS && settings.storage.type !== STORAGE_CUSTOM;
+ const isStorageSync = settings.storage.type !== STORAGE_REDIS && settings.storage.type !== STORAGE_PLUGGABLE;
// instantiate the client
const client = ClientFactory(context);
// Keep a reference to the original methods
diff --git a/src/factory/online.js b/src/factory/online.js
index a0863434b..7aee1aea4 100644
--- a/src/factory/online.js
+++ b/src/factory/online.js
@@ -51,7 +51,7 @@ function SplitFactoryOnline(context, readyTrackers, mainClientMetricCollectors)
break;
}
case CONSUMER_MODE: {
- context.put(context.constants.READY_FROM_CACHE, true); // For SDK inner workings it's supposed to be ready from cache.
+ context.put(context.constants.READY_FROM_CACHE, true); // For SDK inner workings it's supposed to be ready from cache, to be operational for evaluations immediately
break;
}
}
diff --git a/src/listeners/browser.js b/src/listeners/browser.js
index 1cc0534ce..20ec5e685 100644
--- a/src/listeners/browser.js
+++ b/src/listeners/browser.js
@@ -60,13 +60,15 @@ export default class BrowserSignalListener {
* using beacon API if possible, or falling back to regular post transport.
*/
flushData() {
+ if (!this.syncManager) return;
+
this._flushImpressions();
this._flushEvents();
if (this.impressionsCounter) {
this._flushImpressionsCount();
}
// Close streaming
- if (this.syncManager && this.syncManager.pushManager) this.syncManager.pushManager.stop();
+ if (this.syncManager.pushManager) this.syncManager.pushManager.stop();
}
_flushImpressions() {
diff --git a/src/producer/updater/MySegments.js b/src/producer/updater/MySegments.js
index f2a528f98..4a17f3dca 100644
--- a/src/producer/updater/MySegments.js
+++ b/src/producer/updater/MySegments.js
@@ -32,7 +32,7 @@ export default function MySegmentsUpdaterFactory(context) {
let readyOnAlreadyExistentState = true;
let startingUp = true;
- // @TODO if allowing custom storages, handle async execution and wrap errors as SplitErrors to distinguish from user callback errors
+ // @TODO if allowing pluggable storages, handle async execution and wrap errors as SplitErrors to distinguish from user callback errors
function updateSegments(segmentsData) {
const mySegmentsCache = storage.segments;
diff --git a/src/producer/updater/SplitChanges.js b/src/producer/updater/SplitChanges.js
index 4551ec3c5..8dd64ed73 100644
--- a/src/producer/updater/SplitChanges.js
+++ b/src/producer/updater/SplitChanges.js
@@ -85,7 +85,7 @@ export default function SplitChangesUpdaterFactory(context, isNode = false) {
log.debug(`Segment names collected ${mutation.segments}`);
// Write into storage
- // @TODO if allowing custom storages, wrap errors as SplitErrors to distinguish from user callback errors
+ // @TODO wrap errors as SplitErrors or migrate error handling as in JS-commons
return Promise.all([
// calling first `setChangenumber` method, to perform cache flush if split filter queryString changed
storage.splits.setChangeNumber(splitChanges.till),
diff --git a/src/storage/node.js b/src/storage/node.js
index aa306c8db..3f3a9287a 100644
--- a/src/storage/node.js
+++ b/src/storage/node.js
@@ -13,7 +13,7 @@ import EventsCacheInMemory from './EventsCache/InMemory';
import EventsCacheInRedis from './EventsCache/InRedis';
import KeyBuilder from './Keys';
import MetaBuilder from './Meta';
-import { STORAGE_MEMORY, STORAGE_REDIS, STORAGE_CUSTOM } from '../utils/constants';
+import { STORAGE_MEMORY, STORAGE_REDIS, STORAGE_PLUGGABLE } from '../utils/constants';
import { PluggableStorage } from '@splitsoftware/splitio-commons';
import LogFactory from '../utils/logger';
@@ -60,7 +60,7 @@ const NodeStorageFactory = context => {
};
}
- case STORAGE_CUSTOM: {
+ case STORAGE_PLUGGABLE: {
const storageFactory = PluggableStorage(storage);
diff --git a/src/sync/SplitUpdateWorker/index.js b/src/sync/SplitUpdateWorker/index.js
index 3e9560435..27722b9ef 100644
--- a/src/sync/SplitUpdateWorker/index.js
+++ b/src/sync/SplitUpdateWorker/index.js
@@ -67,7 +67,6 @@ export default class SplitUpdateWorker {
* @param {string} defaultTreatment default treatment value
*/
killSplit(changeNumber, splitName, defaultTreatment) {
- // @TODO handle retry due to errors in storage, once we allow the definition of custom async storages
this.splitStorage.killLocally(splitName, defaultTreatment, changeNumber).then((updated) => {
// trigger an SDK_UPDATE if Split was killed locally
if (updated) this.splitsEventEmitter.emit(this.splitsEventEmitter.SDK_SPLITS_ARRIVED, true);
diff --git a/src/utils/constants/index.js b/src/utils/constants/index.js
index 7a8dca494..98e1527b5 100644
--- a/src/utils/constants/index.js
+++ b/src/utils/constants/index.js
@@ -7,7 +7,7 @@ export const CONSUMER_MODE = 'consumer';
export const STORAGE_MEMORY = 'MEMORY';
export const STORAGE_REDIS = 'REDIS';
export const STORAGE_LOCALSTORAGE = 'LOCALSTORAGE';
-export const STORAGE_CUSTOM = 'CUSTOM';
+export const STORAGE_PLUGGABLE = 'PLUGGABLE';
// Special treatments
export const CONTROL = 'control';
export const CONTROL_WITH_CONFIG = {
diff --git a/ts-tests/index.ts b/ts-tests/index.ts
index 36832bc3c..3f038cf67 100644
--- a/ts-tests/index.ts
+++ b/ts-tests/index.ts
@@ -186,7 +186,7 @@ asyncSettings = {
authorizationKey: 'key'
},
storage: {
- type: 'CUSTOM',
+ type: 'PLUGGABLE',
wrapper: new MyWrapper()
}
};
diff --git a/types/index.d.ts b/types/index.d.ts
index eb10a0471..21a3db407 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1,4 +1,4 @@
-// Declaration file for Javascript and Node Split Software SDK v8.1.0
+// Declaration file for Javascript and Node Split Software SDK
// Project: http://www.split.io/
// Definitions by: Nico Zelaya
diff --git a/types/splitio.d.ts b/types/splitio.d.ts
index d15b797ae..baa175214 100644
--- a/types/splitio.d.ts
+++ b/types/splitio.d.ts
@@ -1,4 +1,4 @@
-// Type definitions for Javascript and Node Split Software SDK v8.1.0
+// Type definitions for Javascript and Node Split Software SDK
// Project: http://www.split.io/
// Definitions by: Nico Zelaya
@@ -51,7 +51,7 @@ type SDKMode = 'standalone' | 'consumer';
* Storage types.
* @typedef {string} StorageType
*/
-type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'CUSTOM';
+type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE';
/**
* Settings interface. This is a representation of the settings the SDK expose, that's why
* most of it's props are readonly. Only features should be rewritten when localhost mode is active.
@@ -329,7 +329,7 @@ interface INodeBasicSettings extends ISharedSettings {
*/
options?: Object,
/**
- * Custom storage wrapper. Use it with type: 'CUSTOM'
+ * Storage wrapper. Use it with type: 'PLUGGABLE'
* @property {Object} wrapper
*/
wrapper?: Object,
@@ -624,7 +624,7 @@ declare namespace SplitIO {
* Asynchronous storages valid types for NodeJS.
* @typedef {string} NodeAsyncStorage
*/
- type NodeAsyncStorage = 'REDIS' | 'CUSTOM';
+ type NodeAsyncStorage = 'REDIS' | 'PLUGGABLE';
/**
* Storage valid types for the browser.
* @typedef {string} BrowserStorage
@@ -1037,7 +1037,7 @@ declare namespace SplitIO {
interface INodeAsyncSettings extends INodeBasicSettings {
storage: {
/**
- * Async storage type (Redis or Custom) to be instantiated by the SDK.
+ * Async storage type ('REDIS' or 'PLUGGABLE') to be instantiated by the SDK.
* @property {NodeAsyncStorage} type
*/
type: NodeAsyncStorage,
@@ -1047,7 +1047,7 @@ declare namespace SplitIO {
*/
options?: Object,
/**
- * Custom storage wrapper. Use it with type: 'CUSTOM'
+ * Storage wrapper. Use it with type: 'PLUGGABLE'
* @property {Object} wrapper
*/
wrapper?: Object,