diff --git a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js index 8551d87ae..d5662a292 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.tests.js +++ b/packages/optimizely-sdk/lib/core/decision_service/index.tests.js @@ -30,7 +30,6 @@ import Optimizely from '../../optimizely'; import projectConfig from '../project_config'; import AudienceEvaluator from '../audience_evaluator'; import errorHandler from '../../plugins/error_handler'; -import eventBuilder from '../../core/event_builder/index.js'; import eventDispatcher from '../../plugins/event_dispatcher/index.node'; import * as jsonSchemaValidator from '../../utils/json_schema_validator'; import { @@ -951,7 +950,6 @@ describe('lib/core/decision_service', function() { jsonSchemaValidator: jsonSchemaValidator, isValidInstance: true, logger: createdLogger, - eventBuilder: eventBuilder, eventDispatcher: eventDispatcher, errorHandler: errorHandler, }); diff --git a/packages/optimizely-sdk/lib/optimizely/index.tests.js b/packages/optimizely-sdk/lib/optimizely/index.tests.js index d49aa3cee..58c590ff1 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely/index.tests.js @@ -20,12 +20,12 @@ import eventProcessor from '../core/event_processor'; import * as logging from '@optimizely/js-sdk-logging'; import Optimizely from './'; +import OptimizelyUserContext from '../optimizely_user_context'; import AudienceEvaluator from '../core/audience_evaluator'; import bluebird from 'bluebird'; import bucketer from '../core/bucketer'; import * as projectConfigManager from '../core/project_config/project_config_manager'; import * as enums from '../utils/enums'; -import * as eventBuilder from '../core/event_builder'; import eventDispatcher from '../plugins/event_dispatcher/index.node'; import errorHandler from '../plugins/error_handler'; import fns from '../utils/fns'; @@ -210,7 +210,6 @@ describe('lib/optimizely', function() { it('should not log an error when sdkKey is provided and datafile is not provided', function() { new Optimizely({ clientEngine: 'node-sdk', - eventBuilder: eventBuilder, errorHandler: stubErrorHandler, eventDispatcher: eventDispatcher, isValidInstance: true, @@ -2626,7 +2625,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfig(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -2676,7 +2674,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfig(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -2723,7 +2720,6 @@ describe('lib/optimizely', function() { var optly = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfigWithFeatures(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -4313,6 +4309,95 @@ describe('lib/optimizely', function() { }); }); + describe('decide APIs', function() { + var optlyInstance; + var bucketStub; + var createdLogger = logger.createLogger({ + logLevel: LOG_LEVEL.INFO, + logToConsole: false, + }); + beforeEach(function() { + optlyInstance = new Optimizely({ + clientEngine: 'node-sdk', + datafile: testData.getTestDecideProjectConfig(), + errorHandler: errorHandler, + eventDispatcher: eventDispatcher, + jsonSchemaValidator: jsonSchemaValidator, + logger: createdLogger, + isValidInstance: true, + eventBatchSize: 1, + }); + + bucketStub = sinon.stub(bucketer, 'bucket'); + sinon.stub(errorHandler, 'handleError'); + sinon.stub(createdLogger, 'log'); + sinon.stub(fns, 'uuid').returns('a68cf1ad-0393-4e18-af87-efe8f01a7c9c'); + }); + + afterEach(function() { + bucketer.bucket.restore(); + errorHandler.handleError.restore(); + createdLogger.log.restore(); + fns.uuid.restore(); + }); + describe('#createUserContext', function() { + it('should create OptimizelyUserContext with provided attributes and userId', function() { + var userId = 'testUser1'; + var attributes = { test_attribute: 'test_value' }; + var user = optlyInstance.createUserContext(userId, attributes); + assert.instanceOf(user, OptimizelyUserContext); + assert.deepEqual(optlyInstance, user.getOptimizely()); + assert.deepEqual(attributes, user.getAttributes()); + assert.deepEqual(userId, user.getUserId()); + }); + + it('should create OptimizelyUserContext when no attributes provided', function() { + var userId = 'testUser2'; + var user = optlyInstance.createUserContext(userId); + assert.instanceOf(user, OptimizelyUserContext); + assert.deepEqual(optlyInstance, user.getOptimizely()); + assert.deepEqual({}, user.getAttributes()); + assert.deepEqual(userId, user.getUserId()); + }); + + it('should create multiple instances of OptimizelyUserContext', function() { + var userId1 = 'testUser1' + var userId2 = 'testUser2'; + var attributes1 = { test_attribute: 'test_value' }; + var user1 = optlyInstance.createUserContext(userId1, attributes1); + var user2 = optlyInstance.createUserContext(userId2); + assert.instanceOf(user1, OptimizelyUserContext); + assert.deepEqual(user1.getOptimizely(), optlyInstance); + assert.deepEqual(user1.getAttributes(), attributes1); + assert.deepEqual(user1.getUserId(), userId1); + assert.instanceOf(user2, OptimizelyUserContext); + assert.deepEqual(user2.getOptimizely(), optlyInstance); + assert.deepEqual(user2.getAttributes(), {}); + assert.deepEqual(user2.getUserId(), userId2); + }); + + it('should call the error handler for invalid user ID and return null', function() { + assert.isNull(optlyInstance.createUserContext(null)); + sinon.assert.calledOnce(errorHandler.handleError); + var errorMessage = errorHandler.handleError.lastCall.args[0].message; + assert.strictEqual(errorMessage, sprintf(ERROR_MESSAGES.INVALID_INPUT_FORMAT, 'OPTIMIZELY', 'user_id')); + sinon.assert.calledOnce(createdLogger.log); + var logMessage = createdLogger.log.args[0][1]; + assert.strictEqual(logMessage, sprintf(ERROR_MESSAGES.INVALID_INPUT_FORMAT, 'OPTIMIZELY', 'user_id')); + }); + + it('should call the error handler for invalid attributes and return null', function() { + assert.isNull(optlyInstance.createUserContext('user1', 'invalid_attributes')); + sinon.assert.calledOnce(errorHandler.handleError); + var errorMessage = errorHandler.handleError.lastCall.args[0].message; + assert.strictEqual(errorMessage, sprintf(ERROR_MESSAGES.INVALID_ATTRIBUTES, 'ATTRIBUTES_VALIDATOR')); + sinon.assert.calledOnce(createdLogger.log); + var logMessage = createdLogger.log.args[0][1]; + assert.strictEqual(logMessage, sprintf(ERROR_MESSAGES.INVALID_ATTRIBUTES, 'ATTRIBUTES_VALIDATOR')); + }); + }); + }); + //tests separated out from APIs because of mock bucketing describe('getVariationBucketingIdAttribute', function() { var optlyInstance; @@ -4324,7 +4409,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfig(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -4379,7 +4463,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfigWithFeatures(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -4412,7 +4495,6 @@ describe('lib/optimizely', function() { lasers: 300, message: 'this is not a valid datafile', }, - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -4936,7 +5018,6 @@ describe('lib/optimizely', function() { lasers: 300, message: 'this is not a valid datafile', }, - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -4976,7 +5057,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfigWithFeatures(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -7147,7 +7227,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTypedAudiencesConfig(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -7279,7 +7358,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTypedAudiencesConfig(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -7470,7 +7548,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfig(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -7758,7 +7835,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfig(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -7789,7 +7865,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfig(), - eventBuilder: eventBuilder, errorHandler: errorHandler, eventDispatcher: eventDispatcher, jsonSchemaValidator: jsonSchemaValidator, @@ -8169,7 +8244,6 @@ describe('lib/optimizely', function() { optlyInstance = new Optimizely({ clientEngine: 'node-sdk', datafile: testData.getTestProjectConfig(), - eventBuilder: eventBuilder, errorHandler: { handleError: function() {}, }, diff --git a/packages/optimizely-sdk/lib/optimizely/index.ts b/packages/optimizely-sdk/lib/optimizely/index.ts index 8bcc33f3d..504e002ad 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.ts +++ b/packages/optimizely-sdk/lib/optimizely/index.ts @@ -27,6 +27,7 @@ import { FeatureVariable, OptimizelyOptions } from '../shared_types'; +import OptimizelyUserContext from '../optimizely_user_context'; import { createProjectConfigManager, ProjectConfigManager } from '../core/project_config/project_config_manager'; import { createNotificationCenter, NotificationCenter } from '../core/notification_center'; import { createDecisionService, DecisionService, DecisionObj } from '../core/decision_service'; @@ -55,11 +56,10 @@ const MODULE_NAME = 'OPTIMIZELY'; const DEFAULT_ONREADY_TIMEOUT = 30000; - // TODO: Make feature_key, user_id, variable_key, experiment_key, event_key camelCase -export type InputKey = 'feature_key' | 'user_id' | 'variable_key' | 'experiment_key' | 'event_key' | 'variation_id'; +type InputKey = 'feature_key' | 'user_id' | 'variable_key' | 'experiment_key' | 'event_key' | 'variation_id'; -export type StringInputs = Partial>; +type StringInputs = Partial>; /** * The Optimizely class @@ -1415,4 +1415,29 @@ export default class Optimizely { return Promise.race([this.readyPromise, timeoutPromise]); } + + //============ decide ============// + + /** + * Creates a context of the user for which decision APIs will be called. + * + * A user context will be created successfully even when the SDK is not fully configured yet, so no + * this.isValidInstance() check is performed here. + * + * @param {string} userId The user ID to be used for bucketing. + * @param {UserAttributes} attributes Optional user attributes. + * @return {OptimizelyUserContext|null} An OptimizelyUserContext associated with this OptimizelyClient or + * null if provided inputs are invalid + */ + createUserContext(userId: string, attributes?: UserAttributes): OptimizelyUserContext | null { + if (!this.validateInputs({ user_id: userId }, attributes)) { + return null; + } + + return new OptimizelyUserContext({ + optimizely: this, + userId, + attributes + }); + } } diff --git a/packages/optimizely-sdk/lib/optimizely_user_context/index.tests.js b/packages/optimizely-sdk/lib/optimizely_user_context/index.tests.js new file mode 100644 index 000000000..004a36705 --- /dev/null +++ b/packages/optimizely-sdk/lib/optimizely_user_context/index.tests.js @@ -0,0 +1,162 @@ +/**************************************************************************** + * Copyright 2020, Optimizely, Inc. and contributors * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + ***************************************************************************/ +import { assert } from 'chai'; +import sinon from 'sinon'; +import { sprintf } from '@optimizely/js-sdk-utils'; + + +import OptimizelyUserContext from './'; +import Optimizely from '../optimizely'; + +import logger from '../plugins/logger'; +import eventDispatcher from '../plugins/event_dispatcher/index.node'; +import errorHandler from '../plugins/error_handler'; +import * as jsonSchemaValidator from '../utils/json_schema_validator';; +import { getTestDecideProjectConfig } from '../tests/test_data'; +import { + LOG_LEVEL, + } from '../utils/enums'; + +describe('lib/optimizely_user_context', function() { + describe('APIs', function() { + var createdLogger = logger.createLogger({ + logLevel: LOG_LEVEL.DEBUG, + logToConsole: false, + }); + var optimizely; + beforeEach(function() { + // TODO: replace with fakeOptimizely + optimizely = new Optimizely({ + clientEngine: 'node-sdk', + datafile: getTestDecideProjectConfig(), + errorHandler: errorHandler, + eventDispatcher: eventDispatcher, + jsonSchemaValidator: jsonSchemaValidator, + logger: createdLogger, + isValidInstance: true, + }); + }); + describe('#setAttribute', function() { + it('should set attributes when provided at instantiation of OptimizelyUserContext', function() { + var userId = 'user1'; + var attributes = { test_attribute: 'test_value' }; + var user = new OptimizelyUserContext({ + optimizely, + userId, + attributes + }); + user.setAttribute('k1', {'hello': 'there'}); + user.setAttribute('k2', true); + user.setAttribute('k3', 100); + user.setAttribute('k4', 3.5); + assert.deepEqual(user.getOptimizely(), optimizely); + assert.deepEqual(user.getUserId(), userId); + + var newAttributes = user.getAttributes(); + assert.deepEqual(newAttributes['test_attribute'], 'test_value'); + assert.deepEqual(newAttributes['k1'], {'hello': 'there'}); + assert.deepEqual(newAttributes['k2'], true); + assert.deepEqual(newAttributes['k3'], 100); + assert.deepEqual(newAttributes['k4'], 3.5); + }); + + it('should set attributes when none provided at instantiation of OptimizelyUserContext', function() { + var userId = 'user2'; + var user = new OptimizelyUserContext({ + optimizely, + userId, + }); + user.setAttribute('k1', {'hello': 'there'}); + user.setAttribute('k2', true); + user.setAttribute('k3', 100); + user.setAttribute('k4', 3.5); + assert.deepEqual(user.getOptimizely(), optimizely); + assert.deepEqual(user.getUserId(), userId); + + var newAttributes = user.getAttributes(); + assert.deepEqual(newAttributes['k1'], {'hello': 'there'}); + assert.deepEqual(newAttributes['k2'], true); + assert.deepEqual(newAttributes['k3'], 100); + assert.deepEqual(newAttributes['k4'], 3.5); + }); + + it('should override existing attributes', function() { + var userId = 'user3'; + var attributes = { test_attribute: 'test_value' }; + var user = new OptimizelyUserContext({ + optimizely, + userId, + attributes, + }); + user.setAttribute('k1', {'hello': 'there'}); + user.setAttribute('test_attribute', 'overwritten_value'); + assert.deepEqual(user.getOptimizely(), optimizely); + assert.deepEqual(user.getUserId(), userId); + + var newAttributes = user.getAttributes(); + assert.deepEqual(newAttributes['k1'], {'hello': 'there'}); + assert.deepEqual(newAttributes['test_attribute'], 'overwritten_value'); + assert.deepEqual(Object.keys(newAttributes).length, 2); + }); + + it('should allow to set attributes with value of null', function() { + var userId = 'user4'; + var user = new OptimizelyUserContext({ + optimizely, + userId, + }); + user.setAttribute('null_attribute', null); + assert.deepEqual(user.getOptimizely(), optimizely); + assert.deepEqual(user.getUserId(), userId); + + var newAttributes = user.getAttributes(); + assert.deepEqual(newAttributes['null_attribute'], null); + }); + + it('should set attributes by value in constructor', function() { + var userId = 'user1'; + var attributes = { initial_attribute: 'initial_value' }; + var user = new OptimizelyUserContext({ + optimizely, + userId, + attributes, + }); + attributes["attribute2"] = 100; + assert.deepEqual(user.getAttributes(), { initial_attribute: 'initial_value' }); + user.setAttribute("attribute3", "hello"); + assert.deepEqual(attributes, { initial_attribute: 'initial_value', 'attribute2': 100}); + }); + + it('should not change user attributes if returned by getAttributes object is updated', function() { + var userId = 'user1'; + var attributes = { initial_attribute: 'initial_value' }; + var user = new OptimizelyUserContext({ + optimizely, + userId, + attributes, + }); + var attributes2 = user.getAttributes(); + attributes2['new_attribute'] = { "value": 100 }; + assert.deepEqual(user.getAttributes(), attributes); + var expectedAttributes = { + initial_attribute: 'initial_value', + new_attribute: { "value": 100 } + } + assert.deepEqual(attributes2, expectedAttributes); + }); + }); + }); +}); diff --git a/packages/optimizely-sdk/lib/optimizely_user_context/index.ts b/packages/optimizely-sdk/lib/optimizely_user_context/index.ts new file mode 100644 index 000000000..fc8a26ec8 --- /dev/null +++ b/packages/optimizely-sdk/lib/optimizely_user_context/index.ts @@ -0,0 +1,80 @@ +/**************************************************************************** + * Copyright 2020, Optimizely, Inc. and contributors * + * * + * Licensed under the Apache License, Version 2.0 (the "License"); * + * you may not use this file except in compliance with the License. * + * You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, software * + * distributed under the License is distributed on an "AS IS" BASIS, * + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * + * See the License for the specific language governing permissions and * + * limitations under the License. * + ***************************************************************************/ +import Optimizely from '../../lib/optimizely'; +import { UserAttributes } from '../../lib/shared_types'; + +export default class OptimizelyUserContext { + private optimizely: Optimizely; + private userId: string; + private attributes: UserAttributes; + + constructor({ + optimizely, + userId, + attributes, + }: { + optimizely: Optimizely, + userId: string, + attributes?: UserAttributes, + }) { + this.optimizely = optimizely; + this.userId = userId; + this.attributes = {...attributes} ?? {}; + } + + /** + * Sets an attribute for a given key. + * @param {string} key An attribute key + * @param {any} value An attribute value + */ + setAttribute(key: string, value: unknown): void { + this.attributes[key] = value; + } + + getUserId(): string { + return this.userId; + } + + getAttributes(): UserAttributes { + return {...this.attributes}; + } + + getOptimizely(): Optimizely { + return this.optimizely; + } + + /** + * Returns a decision result for a given flag key and a user context, which contains all data required to deliver the flag. + * If the SDK finds an error, it will return a decision with null for variationKey. The decision will include an error message in reasons. + * @param {string} key A flag key for which a decision will be made. + * @param {OptimizelyDecideOption} options A list of options for decision-making. + * @return {OptimizelyDecision} A decision result. + */ + decide(): void { + //TODO: implement + return; + } + + decideForKeys(): void { + //TODO: implement + return; + } + + decideAll(): void { + //TODO: implement + return; + } +} diff --git a/packages/optimizely-sdk/lib/tests/test_data.js b/packages/optimizely-sdk/lib/tests/test_data.js index b5633f194..722222ccf 100644 --- a/packages/optimizely-sdk/lib/tests/test_data.js +++ b/packages/optimizely-sdk/lib/tests/test_data.js @@ -336,6 +336,365 @@ var config = { projectId: '111001', }; +var decideConfig = { + version: '4', + sendFlagDecisions: true, + rollouts: [ + { + experiments: [ + { + audienceIds: [ + '13389130056' + ], + forcedVariations: {}, + id: '3332020515', + key: '3332020515', + layerId: '3319450668', + status: 'Running', + trafficAllocation: [ + { + endOfRange: 10000, + entityId: '3324490633' + } + ], + variations: [ + { + featureEnabled: true, + id: '3324490633', + key: '3324490633', + variables: [] + } + ] + }, + { + audienceIds: [ + '12208130097' + ], + forcedVariations: {}, + id: '3332020494', + key: '3332020494', + layerId: '3319450668', + status: 'Running', + trafficAllocation: [ + { + endOfRange: 0, + entityId: '3324490562' + } + ], + variations: [ + { + featureEnabled: true, + id: '3324490562', + key: '3324490562', + variables: [] + } + ] + }, + { + status: 'Running', + audienceIds: [], + variations: [ + { + variables: [], + id: '18257766532', + key: '18257766532', + featureEnabled: true + } + ], + id: '18322080788', + key: '18322080788', + layerId: '18263344648', + trafficAllocation: [ + { + entityId: '18257766532', + endOfRange: 10000 + } + ], + forcedVariations: {} + } + ], + id: '3319450668' + } + ], + anonymizeIP: true, + botFiltering: true, + projectId: '10431130345', + variables: [], + featureFlags: [ + { + experimentIds: [ + '10390977673' + ], + id: '4482920077', + key: 'feature_1', + rolloutId: '3319450668', + variables: [ + { + defaultValue: '42', + id: '2687470095', + key: 'i_42', + type: 'integer' + }, + { + defaultValue: '4.2', + id: '2689280165', + key: 'd_4_2', + type: 'double' + }, + { + defaultValue: 'true', + id: '2689660112', + key: 'b_true', + type: 'boolean' + }, + { + defaultValue: 'foo', + id: '2696150066', + key: 's_foo', + type: 'string' + }, + { + defaultValue: { + value: 1 + }, + id: '2696150067', + key: 'j_1', + type: 'string', + subType: 'json' + }, + { + defaultValue: 'invalid', + id: '2696150068', + key: 'i_1', + type: 'invalid', + subType: '' + } + ] + }, + { + experimentIds: [ + '10420810910' + ], + id: '4482920078', + key: 'feature_2', + rolloutId: '', + variables: [ + { + defaultValue: '42', + id: '2687470095', + key: 'i_42', + type: 'integer' + } + ] + }, + { + experimentIds: [], + id: '44829230000', + key: 'feature_3', + rolloutId: '', + variables: [] + } + ], + experiments: [ + { + status: 'Running', + key: 'exp_with_audience', + layerId: '10420273888', + trafficAllocation: [ + { + entityId: '10389729780', + endOfRange: 10000 + } + ], + audienceIds: [ + '13389141123' + ], + variations: [ + { + variables: [], + featureEnabled: true, + id: '10389729780', + key: 'a' + }, + { + variables: [], + id: '10416523121', + key: 'b' + } + ], + forcedVariations: {}, + id: '10390977673' + }, + { + status: 'Running', + key: 'exp_no_audience', + layerId: '10417730432', + trafficAllocation: [ + { + entityId: '10418551353', + endOfRange: 10000 + } + ], + audienceIds: [], + variations: [ + { + variables: [], + featureEnabled: true, + id: '10418551353', + key: 'variation_with_traffic' + }, + { + variables: [], + featureEnabled: false, + id: '10418510624', + key: 'variation_no_traffic' + } + ], + forcedVariations: {}, + id: '10420810910' + } + ], + audiences: [ + { + id: '13389141123', + conditions: '["and",["or",["or",{ "match": "exact", "name": "gender", "type": "custom_attribute", "value": "f"}]]]', + name: 'gender' + }, + { + id: '13389130056', + conditions: '["and",["or",["or",{ "match": "exact","name": "country","type": "custom_attribute","value": "US"}]]]', + name: 'US' + }, + { + id: '12208130097', + conditions: '["and",["or",["or",{"match": "exact","name": "browser","type": "custom_attribute","value": "safari"}]]]', + name: 'safari' + }, + { + id: "age_18", + conditions: '["and",["or",["or",{"match": "gt","name": "age","type": "custom_attribute","value": 18}]]]', + name: 'age_18' + }, + { + id: 'invalid_format', + conditions: '[]', + name: 'invalid_format' + }, + { + id: 'invalid_condition', + conditions: '["and",["or",["or",{"match": "gt","name": "age","type": "custom_attribute","value": "US"}]]]', + name: 'invalid_condition' + }, + { + id: 'invalid_type', + conditions: '["and",["or",["or",{"match": "gt","name": "age","type": "invalid","value": 18}]]]', + name: 'invalid_type' + }, + { + id: 'invalid_match', + conditions: '["and",["or",["or",{"match": "invalid","name": "age","type": "custom_attribute","value": 18}]]]', + name: 'invalid_match' + }, + { + id: 'nil_value', + conditions: '["and",["or",["or",{"match": "gt","name": "age","type": "custom_attribute"}]]]', + name: 'nil_value' + }, + { + id: 'invalid_name', + conditions: '["and",["or",["or",{"match": "gt","type": "custom_attribute","value": 18}]]]', + name: 'invalid_name' + } + ], + groups: [ + { + policy: 'random', + trafficAllocation: [ + { + entityId: '10390965532', + endOfRange: 10000 + } + ], + experiments: [ + { + status: 'Running', + key: 'group_exp_1', + layerId: '10420222423', + trafficAllocation: [ + { + entityId: '10389752311', + endOfRange: 10000 + } + ], + audienceIds: [], + variations: [ + { + variables: [], + featureEnabled: false, + id: '10389752311', + key: 'a' + } + ], + forcedVariations: {}, + id: '10390965532' + }, + { + status: 'Running', + key: 'group_exp_2', + layerId: '10417730432', + trafficAllocation: [ + { + entityId: '10418524243', + endOfRange: 10000 + } + ], + audienceIds: [], + variations: [ + { + variables: [], + featureEnabled: false, + id: '10418524243', + key: 'a' + } + ], + forcedVariations: {}, + id: '10420843432' + } + ], + id: '13142870430' + } + ], + attributes: [ + { + id: '10401066117', + key: 'gender' + }, + { + id: '10401066170', + key: 'testvar' + } + ], + accountId: '10367498574', + events: [ + { + experimentIds: [ + '10420810910' + ], + id: '10404198134', + key: 'event1' + }, + { + experimentIds: [ + '10420810910', + '10390977673' + ], + id: '10404198135', + key: 'event_multiple_running_exp_attached' + } + ], + revision: '241' +}; + export var getParsedAudiences = [ { name: 'Firefox users', @@ -348,6 +707,10 @@ export var getTestProjectConfig = function() { return cloneDeep(config); }; +export var getTestDecideProjectConfig = function() { + return cloneDeep(decideConfig); +}; + var configWithFeatures = { events: [ { @@ -2729,6 +3092,7 @@ export var featureTestDecisionObj = { export default { getTestProjectConfig: getTestProjectConfig, + getTestDecideProjectConfig: getTestDecideProjectConfig, getParsedAudiences: getParsedAudiences, getTestProjectConfigWithFeatures: getTestProjectConfigWithFeatures, datafileWithFeaturesExpectedData: datafileWithFeaturesExpectedData, diff --git a/packages/optimizely-sdk/lib/utils/enums/index.ts b/packages/optimizely-sdk/lib/utils/enums/index.ts index dc71b6e91..48bf91dd6 100644 --- a/packages/optimizely-sdk/lib/utils/enums/index.ts +++ b/packages/optimizely-sdk/lib/utils/enums/index.ts @@ -229,8 +229,8 @@ export const DATAFILE_VERSIONS = { }; /* -* Pre-Release and Build symbols -*/ + * Pre-Release and Build symbols + */ export const enum VERSION_TYPE { PRE_RELEASE_VERSION_DELIMITER = '-', BUILD_VERSION_DELIMITER = '+'