From 10ca7b3e9ef6596d096434dc9ae7d4acb398c8c3 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Sat, 2 Dec 2023 02:35:29 +0600 Subject: [PATCH 1/6] [FSSDK-9778] return last experiment when duplicate key in config --- lib/core/optimizely_config/index.tests.js | 39 +++- lib/core/optimizely_config/index.ts | 83 ++++--- .../project_config/project_config_manager.ts | 8 +- lib/optimizely/index.ts | 1 + lib/tests/test_data.js | 203 ++++++++++++++++++ 5 files changed, 299 insertions(+), 35 deletions(-) diff --git a/lib/core/optimizely_config/index.tests.js b/lib/core/optimizely_config/index.tests.js index 25ce515fe..39c8a3e5d 100644 --- a/lib/core/optimizely_config/index.tests.js +++ b/lib/core/optimizely_config/index.tests.js @@ -15,6 +15,7 @@ */ import { assert } from 'chai'; import { cloneDeep } from 'lodash'; +import sinon from 'sinon'; import { createOptimizelyConfig, OptimizelyConfig } from './'; import { createProjectConfig } from '../project_config'; @@ -22,9 +23,13 @@ import { getTestProjectConfigWithFeatures, getTypedAudiencesConfig, getSimilarRuleKeyConfig, - getSimilarExperimentKeyConfig + getSimilarExperimentKeyConfig, + getDuplicateExperimentKeyConfig, } from '../../tests/test_data'; +import * as logging from '../../modules/logging'; +import { log } from 'console'; + var datafile = getTestProjectConfigWithFeatures(); var typedAudienceDatafile = getTypedAudiencesConfig(); var similarRuleKeyDatafile = getSimilarRuleKeyConfig(); @@ -53,6 +58,13 @@ describe('lib/core/optimizely_config', function() { var projectSimilarRuleKeyConfigObject; var optimizelySimilarExperimentkeyConfigObject; var projectSimilarExperimentKeyConfigObject; + + const fakeLogger = { + warn: () => {}, + }; + + let loggingStub; + beforeEach(function() { projectConfigObject = createProjectConfig(cloneDeep(datafile)); optimizelyConfigObject = createOptimizelyConfig(projectConfigObject, JSON.stringify(datafile)); @@ -62,6 +74,13 @@ describe('lib/core/optimizely_config', function() { optimizelySimilarRuleKeyConfigObject = createOptimizelyConfig(projectSimilarRuleKeyConfigObject, JSON.stringify(similarRuleKeyDatafile)); projectSimilarExperimentKeyConfigObject = createProjectConfig(cloneDeep(similarExperimentKeyDatafile)); optimizelySimilarExperimentkeyConfigObject = createOptimizelyConfig(projectSimilarExperimentKeyConfigObject, JSON.stringify(similarExperimentKeyDatafile)); + loggingStub = sinon.stub(logging, 'getLogger').returns(fakeLogger); + sinon.spy(fakeLogger, 'warn'); + }); + + this.afterEach(function() { + loggingStub.restore(); + fakeLogger.warn.restore(); }); it('should return all experiments except rollouts', function() { @@ -85,6 +104,24 @@ describe('lib/core/optimizely_config', function() { }); }); + it('should keep the last experiment in case of duplicate key and log a warning', function() { + const datafile = getDuplicateExperimentKeyConfig(); + const configObj = createProjectConfig(datafile, JSON.stringify(datafile)); + + const logger = { + warn: sinon.spy(), + } + + const optimizelyConfig = createOptimizelyConfig(configObj, JSON.stringify(datafile), logger); + const experimentsMap = optimizelyConfig.experimentsMap; + + const duplicateKey = 'experiment_rule'; + const lastExperiment = datafile.experiments[datafile.experiments.length - 1]; + + assert.equal(experimentsMap['experiment_rule'].id, lastExperiment.id); + assert.isTrue(logger.warn.calledWithExactly(`Duplicate experiment keys found in datafile: ${duplicateKey}`)); + }); + it('should return all the feature flags', function() { var featureFlagsCount = Object.keys(optimizelyConfigObject.featuresMap).length; assert.equal(featureFlagsCount, 9); diff --git a/lib/core/optimizely_config/index.ts b/lib/core/optimizely_config/index.ts index 8ea70ecce..01b757d06 100644 --- a/lib/core/optimizely_config/index.ts +++ b/lib/core/optimizely_config/index.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { LoggerFacade, getLogger } from '../../modules/logging'; import { ProjectConfig } from '../project_config'; import { DEFAULT_OPERATOR_TYPES } from '../condition_tree_evaluator'; import { @@ -61,13 +62,16 @@ export class OptimizelyConfig { public events: OptimizelyEvent[]; private datafile: string; - constructor(configObj: ProjectConfig, datafile: string) { + private logger?: LoggerFacade; + + constructor(configObj: ProjectConfig, datafile: string, logger?: LoggerFacade) { this.sdkKey = configObj.sdkKey ?? ''; this.environmentKey = configObj.environmentKey ?? ''; this.attributes = configObj.attributes; this.audiences = OptimizelyConfig.getAudiences(configObj); this.events = configObj.events; this.revision = configObj.revision; + this.logger = logger; const featureIdVariablesMap = (configObj.featureFlags || []).reduce((resultMap: FeatureVariablesMap, feature) => { resultMap[feature.id] = feature.variables; @@ -76,10 +80,12 @@ export class OptimizelyConfig { const variableIdMap = OptimizelyConfig.getVariableIdMap(configObj); - const experimentsMapById = OptimizelyConfig.getExperimentsMapById( - configObj, featureIdVariablesMap, variableIdMap + const { experimentsMapById, experimentsMapByKey } = OptimizelyConfig.getExperimentsMap( + configObj, featureIdVariablesMap, variableIdMap, this.logger, ); - this.experimentsMap = OptimizelyConfig.getExperimentsKeyMap(experimentsMapById); + + this.experimentsMap = experimentsMapByKey; + // this.experimentsMap = OptimizelyConfig.getExperimentsKeyMap(experimentsMapById); this.featuresMap = OptimizelyConfig.getFeaturesMap( configObj, featureIdVariablesMap, experimentsMapById, variableIdMap ); @@ -347,39 +353,52 @@ export class OptimizelyConfig { * @param {ProjectConfig} configObj * @param {FeatureVariablesMap} featureIdVariableMap * @param {{[id: string]: FeatureVariable}} variableIdMap - * @returns {[id: string]: OptimizelyExperiment} Experiments mapped by id + * @returns { experimentsMapById: { [id: string]: OptimizelyExperiment }, experimentsMapByKey: OptimizelyExperimentsMap } Experiments mapped by id and key */ - static getExperimentsMapById( + static getExperimentsMap( configObj: ProjectConfig, featureIdVariableMap: FeatureVariablesMap, - variableIdMap: {[id: string]: FeatureVariable} - ): { [id: string]: OptimizelyExperiment } { + variableIdMap: {[id: string]: FeatureVariable}, + logger?: LoggerFacade, + ) : { experimentsMapById: { [id: string]: OptimizelyExperiment }, experimentsMapByKey: OptimizelyExperimentsMap } { const rolloutExperimentIds = this.getRolloutExperimentIds(configObj.rollouts); - const experiments = configObj.experiments; + const experimentsMapById: { [id : string]: OptimizelyExperiment } = {}; + const experimentsMapByKey: OptimizelyExperimentsMap = {}; - return (experiments || []).reduce((experimentsMap: { [id: string]: OptimizelyExperiment }, experiment) => { - if (rolloutExperimentIds.indexOf(experiment.id) === -1) { - const featureIds = configObj.experimentFeatureMap[experiment.id]; - let featureId = ''; - if (featureIds && featureIds.length > 0) { - featureId = featureIds[0]; - } - const variationsMap = OptimizelyConfig.getVariationsMap( - experiment.variations, - featureIdVariableMap, - variableIdMap, - featureId.toString() - ); - experimentsMap[experiment.id] = { - id: experiment.id, - key: experiment.key, - audiences: OptimizelyConfig.getExperimentAudiences(experiment, configObj), - variationsMap: variationsMap, - }; + const experiments = configObj.experiments || []; + experiments.forEach((experiment) => { + if (rolloutExperimentIds.indexOf(experiment.id) !== -1) { + return; } - return experimentsMap; - }, {}); + + const featureIds = configObj.experimentFeatureMap[experiment.id]; + let featureId = ''; + if (featureIds && featureIds.length > 0) { + featureId = featureIds[0]; + } + const variationsMap = OptimizelyConfig.getVariationsMap( + experiment.variations, + featureIdVariableMap, + variableIdMap, + featureId.toString() + ); + + const optimizelyExperiment: OptimizelyExperiment = { + id: experiment.id, + key: experiment.key, + audiences: OptimizelyConfig.getExperimentAudiences(experiment, configObj), + variationsMap: variationsMap, + }; + + experimentsMapById[experiment.id] = optimizelyExperiment; + if (experimentsMapByKey[experiment.key] && logger) { + logger.warn(`Duplicate experiment keys found in datafile: ${experiment.key}`); + } + experimentsMapByKey[experiment.key] = optimizelyExperiment; + }); + + return { experimentsMapById, experimentsMapByKey }; } /** @@ -461,6 +480,6 @@ export class OptimizelyConfig { * @param {string} datafile * @returns {OptimizelyConfig} An instance of OptimizelyConfig */ -export function createOptimizelyConfig(configObj: ProjectConfig, datafile: string): OptimizelyConfig { - return new OptimizelyConfig(configObj, datafile); +export function createOptimizelyConfig(configObj: ProjectConfig, datafile: string, logger?: LoggerFacade): OptimizelyConfig { + return new OptimizelyConfig(configObj, datafile, logger); } diff --git a/lib/core/project_config/project_config_manager.ts b/lib/core/project_config/project_config_manager.ts index 21026679d..61ffd0b0b 100644 --- a/lib/core/project_config/project_config_manager.ts +++ b/lib/core/project_config/project_config_manager.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { getLogger } from '../../modules/logging'; +import { LoggerFacade, getLogger } from '../../modules/logging'; import { sprintf } from '../../utils/fns'; import { ERROR_MESSAGES } from '../../utils/enums'; @@ -33,6 +33,7 @@ interface ProjectConfigManagerConfig { }; sdkKey?: string; datafileManager?: DatafileManager; + logger?: LoggerFacade; } /** @@ -65,8 +66,11 @@ export class ProjectConfigManager { public jsonSchemaValidator: { validate(jsonObject: unknown): boolean } | undefined; public datafileManager: DatafileManager | null = null; + private logger?: LoggerFacade; + constructor(config: ProjectConfigManagerConfig) { try { + this.logger = config.logger; this.jsonSchemaValidator = config.jsonSchemaValidator; if (!config.datafile && !config.sdkKey) { @@ -210,7 +214,7 @@ export class ProjectConfigManager { */ getOptimizelyConfig(): OptimizelyConfig | null { if (!this.optimizelyConfigObj && this.configObj) { - this.optimizelyConfigObj = createOptimizelyConfig(this.configObj, toDatafile(this.configObj)); + this.optimizelyConfigObj = createOptimizelyConfig(this.configObj, toDatafile(this.configObj), logger); } return this.optimizelyConfigObj; } diff --git a/lib/optimizely/index.ts b/lib/optimizely/index.ts index c2b26478e..c9735ffb7 100644 --- a/lib/optimizely/index.ts +++ b/lib/optimizely/index.ts @@ -131,6 +131,7 @@ export default class Optimizely implements Client { jsonSchemaValidator: config.jsonSchemaValidator, sdkKey: config.sdkKey, datafileManager: config.datafileManager, + logger: this.logger }); this.disposeOnUpdate = this.projectConfigManager.onUpdate((configObj: projectConfig.ProjectConfig) => { diff --git a/lib/tests/test_data.js b/lib/tests/test_data.js index 4ac9a1b37..e2d196967 100644 --- a/lib/tests/test_data.js +++ b/lib/tests/test_data.js @@ -3951,6 +3951,208 @@ export var getSimilarExperimentKeyConfig = function() { return cloneDeep(similarExperimentKeysConfig); }; +const duplicateExperimentKeyConfig = { + "accountId": "23793010390", + "projectId": "24812320344", + "revision": "24", + "attributes": [ + { + "id": "24778491463", + "key": "country" + }, + { + "id": "24802951640", + "key": "likes_donuts" + } + ], + "audiences": [ + { + "name": "mutext_feat", + "conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]", + "id": "24837020039" + }, + { + "id": "$opt_dummy_audience", + "name": "Optimizely-Generated Audience for Backwards Compatibility", + "conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]" + } + ], + "version": "4", + "events": [], + "integrations": [], + "anonymizeIP": true, + "botFiltering": false, + "typedAudiences": [ + { + "name": "mutext_feat", + "conditions": [ + "and", + [ + "or", + [ + "or", + { + "match": "exact", + "name": "country", + "type": "custom_attribute", + "value": "US" + } + ], + [ + "or", + { + "match": "exact", + "name": "likes_donuts", + "type": "custom_attribute", + "value": true + } + ] + ] + ], + "id": "24837020039" + } + ], + "variables": [], + "environmentKey": "production", + "sdkKey": "BBhivmjEBF1KLK8HkMrvj", + "featureFlags": [ + { + "id": "101043", + "key": "mutext_feat2", + "rolloutId": "rollout-101043-24783691394", + "experimentIds": [ + "9300000361925" + ], + "variables": [] + }, + { + "id": "101044", + "key": "mutex_feat", + "rolloutId": "rollout-101044-24783691394", + "experimentIds": [ + "9300000365056" + ], + "variables": [] + } + ], + "rollouts": [ + { + "id": "rollout-101043-24783691394", + "experiments": [ + { + "id": "default-rollout-101043-24783691394", + "key": "default-rollout-101043-24783691394", + "status": "Running", + "layerId": "rollout-101043-24783691394", + "variations": [ + { + "id": "321340", + "key": "off", + "featureEnabled": false, + "variables": [] + } + ], + "trafficAllocation": [ + { + "entityId": "321340", + "endOfRange": 10000 + } + ], + "forcedVariations": {}, + "audienceIds": [], + "audienceConditions": [] + } + ] + }, + { + "id": "rollout-101044-24783691394", + "experiments": [ + { + "id": "default-rollout-101044-24783691394", + "key": "default-rollout-101044-24783691394", + "status": "Running", + "layerId": "rollout-101044-24783691394", + "variations": [ + { + "id": "321343", + "key": "off", + "featureEnabled": false, + "variables": [] + } + ], + "trafficAllocation": [ + { + "entityId": "321343", + "endOfRange": 10000 + } + ], + "forcedVariations": {}, + "audienceIds": [], + "audienceConditions": [] + } + ] + } + ], + "experiments": [ + { + "id": "9300000361925", + "key": "experiment_rule", + "status": "Running", + "layerId": "9300000284731", + "variations": [ + { + "id": "321342", + "key": "variation_1", + "featureEnabled": true, + "variables": [] + } + ], + "trafficAllocation": [ + { + "entityId": "321342", + "endOfRange": 10000 + } + ], + "forcedVariations": {}, + "audienceIds": [ + "24837020039" + ], + "audienceConditions": [ + "or", + "24837020039" + ] + }, + { + "id": "9300000365056", + "key": "experiment_rule", + "status": "Running", + "layerId": "9300000287826", + "variations": [ + { + "id": "321345", + "key": "variation_1", + "featureEnabled": true, + "variables": [] + } + ], + "trafficAllocation": [ + { + "entityId": "321345", + "endOfRange": 10000 + } + ], + "forcedVariations": {}, + "audienceIds": [], + "audienceConditions": [] + } + ], + "groups": [] +}; + +export const getDuplicateExperimentKeyConfig = function() { + return cloneDeep(duplicateExperimentKeyConfig); +}; + export default { getTestProjectConfig: getTestProjectConfig, getTestDecideProjectConfig: getTestDecideProjectConfig, @@ -3966,4 +4168,5 @@ export default { getMutexFeatureTestsConfig: getMutexFeatureTestsConfig, getSimilarRuleKeyConfig: getSimilarRuleKeyConfig, getSimilarExperimentKeyConfig: getSimilarExperimentKeyConfig, + getDuplicateExperimentKeyConfig: getDuplicateExperimentKeyConfig, }; From d8015807cbe99363b1613f01f4b43ae64029977f Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Sat, 2 Dec 2023 02:39:04 +0600 Subject: [PATCH 2/6] cleanup --- lib/core/optimizely_config/index.tests.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/lib/core/optimizely_config/index.tests.js b/lib/core/optimizely_config/index.tests.js index 39c8a3e5d..9f147c1b5 100644 --- a/lib/core/optimizely_config/index.tests.js +++ b/lib/core/optimizely_config/index.tests.js @@ -27,9 +27,6 @@ import { getDuplicateExperimentKeyConfig, } from '../../tests/test_data'; -import * as logging from '../../modules/logging'; -import { log } from 'console'; - var datafile = getTestProjectConfigWithFeatures(); var typedAudienceDatafile = getTypedAudiencesConfig(); var similarRuleKeyDatafile = getSimilarRuleKeyConfig(); @@ -59,12 +56,6 @@ describe('lib/core/optimizely_config', function() { var optimizelySimilarExperimentkeyConfigObject; var projectSimilarExperimentKeyConfigObject; - const fakeLogger = { - warn: () => {}, - }; - - let loggingStub; - beforeEach(function() { projectConfigObject = createProjectConfig(cloneDeep(datafile)); optimizelyConfigObject = createOptimizelyConfig(projectConfigObject, JSON.stringify(datafile)); @@ -74,13 +65,6 @@ describe('lib/core/optimizely_config', function() { optimizelySimilarRuleKeyConfigObject = createOptimizelyConfig(projectSimilarRuleKeyConfigObject, JSON.stringify(similarRuleKeyDatafile)); projectSimilarExperimentKeyConfigObject = createProjectConfig(cloneDeep(similarExperimentKeyDatafile)); optimizelySimilarExperimentkeyConfigObject = createOptimizelyConfig(projectSimilarExperimentKeyConfigObject, JSON.stringify(similarExperimentKeyDatafile)); - loggingStub = sinon.stub(logging, 'getLogger').returns(fakeLogger); - sinon.spy(fakeLogger, 'warn'); - }); - - this.afterEach(function() { - loggingStub.restore(); - fakeLogger.warn.restore(); }); it('should return all experiments except rollouts', function() { From c52d2b203ec166f778b4bf7602c4cd13f3a4f962 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 4 Dec 2023 19:03:37 +0600 Subject: [PATCH 3/6] update unit test --- lib/optimizely/index.tests.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/optimizely/index.tests.js b/lib/optimizely/index.tests.js index 8c226493b..69779e3d2 100644 --- a/lib/optimizely/index.tests.js +++ b/lib/optimizely/index.tests.js @@ -292,6 +292,7 @@ describe('lib/optimizely', function() { jsonSchemaValidator: jsonSchemaValidator, sdkKey: '12345', datafileManager: datafileManager, + logger: createdLogger, }); }); }); From 08e05f2cd5cc6c97457b49bfa59a9b3bd6f6d97d Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 4 Dec 2023 19:25:37 +0600 Subject: [PATCH 4/6] don't store logger in OptimizelyConfig class field --- lib/core/optimizely_config/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/core/optimizely_config/index.ts b/lib/core/optimizely_config/index.ts index 01b757d06..f3674d17f 100644 --- a/lib/core/optimizely_config/index.ts +++ b/lib/core/optimizely_config/index.ts @@ -62,7 +62,6 @@ export class OptimizelyConfig { public events: OptimizelyEvent[]; private datafile: string; - private logger?: LoggerFacade; constructor(configObj: ProjectConfig, datafile: string, logger?: LoggerFacade) { this.sdkKey = configObj.sdkKey ?? ''; @@ -71,7 +70,6 @@ export class OptimizelyConfig { this.audiences = OptimizelyConfig.getAudiences(configObj); this.events = configObj.events; this.revision = configObj.revision; - this.logger = logger; const featureIdVariablesMap = (configObj.featureFlags || []).reduce((resultMap: FeatureVariablesMap, feature) => { resultMap[feature.id] = feature.variables; @@ -81,7 +79,7 @@ export class OptimizelyConfig { const variableIdMap = OptimizelyConfig.getVariableIdMap(configObj); const { experimentsMapById, experimentsMapByKey } = OptimizelyConfig.getExperimentsMap( - configObj, featureIdVariablesMap, variableIdMap, this.logger, + configObj, featureIdVariablesMap, variableIdMap, logger, ); this.experimentsMap = experimentsMapByKey; From 7e7d1cb9b7931b22b78a7fcdcbab6549cc6ec423 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 4 Dec 2023 21:15:34 +0600 Subject: [PATCH 5/6] remove logger injection --- lib/core/project_config/project_config_manager.ts | 6 +----- lib/optimizely/index.tests.js | 1 - lib/optimizely/index.ts | 1 - 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/core/project_config/project_config_manager.ts b/lib/core/project_config/project_config_manager.ts index 61ffd0b0b..0432b5fc1 100644 --- a/lib/core/project_config/project_config_manager.ts +++ b/lib/core/project_config/project_config_manager.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { LoggerFacade, getLogger } from '../../modules/logging'; +import { getLogger } from '../../modules/logging'; import { sprintf } from '../../utils/fns'; import { ERROR_MESSAGES } from '../../utils/enums'; @@ -33,7 +33,6 @@ interface ProjectConfigManagerConfig { }; sdkKey?: string; datafileManager?: DatafileManager; - logger?: LoggerFacade; } /** @@ -66,11 +65,8 @@ export class ProjectConfigManager { public jsonSchemaValidator: { validate(jsonObject: unknown): boolean } | undefined; public datafileManager: DatafileManager | null = null; - private logger?: LoggerFacade; - constructor(config: ProjectConfigManagerConfig) { try { - this.logger = config.logger; this.jsonSchemaValidator = config.jsonSchemaValidator; if (!config.datafile && !config.sdkKey) { diff --git a/lib/optimizely/index.tests.js b/lib/optimizely/index.tests.js index 69779e3d2..8c226493b 100644 --- a/lib/optimizely/index.tests.js +++ b/lib/optimizely/index.tests.js @@ -292,7 +292,6 @@ describe('lib/optimizely', function() { jsonSchemaValidator: jsonSchemaValidator, sdkKey: '12345', datafileManager: datafileManager, - logger: createdLogger, }); }); }); diff --git a/lib/optimizely/index.ts b/lib/optimizely/index.ts index c9735ffb7..c2b26478e 100644 --- a/lib/optimizely/index.ts +++ b/lib/optimizely/index.ts @@ -131,7 +131,6 @@ export default class Optimizely implements Client { jsonSchemaValidator: config.jsonSchemaValidator, sdkKey: config.sdkKey, datafileManager: config.datafileManager, - logger: this.logger }); this.disposeOnUpdate = this.projectConfigManager.onUpdate((configObj: projectConfig.ProjectConfig) => { From d3a75ba895490784b23028452a1cbda854c01770 Mon Sep 17 00:00:00 2001 From: Raju Ahmed Date: Mon, 4 Dec 2023 21:19:54 +0600 Subject: [PATCH 6/6] remove comment --- lib/core/optimizely_config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/optimizely_config/index.ts b/lib/core/optimizely_config/index.ts index f3674d17f..4b435b830 100644 --- a/lib/core/optimizely_config/index.ts +++ b/lib/core/optimizely_config/index.ts @@ -83,7 +83,7 @@ export class OptimizelyConfig { ); this.experimentsMap = experimentsMapByKey; - // this.experimentsMap = OptimizelyConfig.getExperimentsKeyMap(experimentsMapById); + this.featuresMap = OptimizelyConfig.getFeaturesMap( configObj, featureIdVariablesMap, experimentsMapById, variableIdMap );