Skip to content

Commit dfc1b7f

Browse files
authored
feat: Convert optimizely_config module to TS (#626)
* Initial conversion * Convert optimizely_config to TS * Fix unit tests * Clean up * Use featureId as any * Clean up * Remove any in getMergedVariablesMap method * change datafile to back to __datafile to fix fsc * Confirm fsc tests failure do to name change from __datafile to datafile. Update to datafile * Use ProjectConfig interface instead of OptimizelyConfigOptions * Change private methods to static * Clean up
1 parent b9b81c4 commit dfc1b7f

File tree

7 files changed

+264
-164
lines changed

7 files changed

+264
-164
lines changed

packages/optimizely-sdk/lib/core/optimizely_config/index.js

Lines changed: 0 additions & 135 deletions
This file was deleted.

packages/optimizely-sdk/lib/core/optimizely_config/index.tests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import { assert } from 'chai';
1717
import { cloneDeep } from 'lodash';
1818

19-
import { OptimizelyConfig } from './index';
19+
import { createOptimizelyConfig } from './';
2020
import { createProjectConfig } from '../project_config';
2121
import { getTestProjectConfigWithFeatures } from '../../tests/test_data';
2222

@@ -41,7 +41,7 @@ describe('lib/core/optimizely_config', function() {
4141
var projectConfigObject;
4242
beforeEach(function() {
4343
projectConfigObject = createProjectConfig(cloneDeep(datafile));
44-
optimizelyConfigObject = new OptimizelyConfig(projectConfigObject, JSON.stringify(datafile));
44+
optimizelyConfigObject = createOptimizelyConfig(projectConfigObject, JSON.stringify(datafile));
4545
});
4646

4747
it('should return all experiments except rollouts', function() {
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/**
2+
* Copyright 2020, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { isFeatureExperiment, ProjectConfig } from '../project_config';
17+
import {
18+
OptimizelyExperimentsMap,
19+
OptimizelyFeaturesMap,
20+
OptimizelyVariablesMap,
21+
FeatureVariable,
22+
VariationVariable,
23+
Variation,
24+
Rollout,
25+
} from '../../shared_types';
26+
27+
interface FeatureVariablesMap {
28+
[key: string]: FeatureVariable[];
29+
}
30+
31+
/**
32+
* The OptimizelyConfig class
33+
* @param {ProjectConfig} configObj
34+
* @param {string} datafile
35+
*/
36+
export class OptimizelyConfig {
37+
public experimentsMap: OptimizelyExperimentsMap;
38+
public featuresMap: OptimizelyFeaturesMap;
39+
public revision: string;
40+
private datafile: string;
41+
42+
constructor(configObj: ProjectConfig, datafile: string) {
43+
this.experimentsMap = OptimizelyConfig.getExperimentsMap(configObj);
44+
this.featuresMap = OptimizelyConfig.getFeaturesMap(configObj, this.experimentsMap);
45+
this.revision = configObj.revision;
46+
this.datafile = datafile;
47+
}
48+
49+
/**
50+
* Get the datafile
51+
* @returns {string} JSON string representation of the datafile that was used to create the current config object
52+
*/
53+
getDatafile(): string {
54+
return this.datafile;
55+
}
56+
57+
/**
58+
* Get Experiment Ids which are part of rollout
59+
* @param {Rollout[]} rollouts
60+
* @returns {[key: string]: boolean} Map of experiment Ids to boolean
61+
*/
62+
static getRolloutExperimentIds(rollouts: Rollout[]): { [key: string]: boolean } {
63+
return (rollouts || []).reduce((experimentIds: { [key: string]: boolean }, rollout) => {
64+
rollout.experiments.forEach((e) => {
65+
(experimentIds)[e.id] = true;
66+
});
67+
68+
return experimentIds;
69+
}, {});
70+
}
71+
72+
/**
73+
* Get Map of all experiments except rollouts
74+
* @param {ProjectConfig} configObj
75+
* @returns {OptimizelyExperimentsMap} Map of experiments excluding rollouts
76+
*/
77+
static getExperimentsMap(configObj: ProjectConfig): OptimizelyExperimentsMap {
78+
const rolloutExperimentIds = this.getRolloutExperimentIds(configObj.rollouts);
79+
const featureVariablesMap = (configObj.featureFlags || []).reduce(
80+
(resultMap: FeatureVariablesMap, feature) => {
81+
resultMap[feature.id] = feature.variables;
82+
return resultMap;
83+
},
84+
{},
85+
);
86+
87+
return (configObj.experiments || []).reduce(
88+
(experiments: OptimizelyExperimentsMap, experiment) => {
89+
// skip experiments that are part of a rollout
90+
if (!rolloutExperimentIds[experiment.id]) {
91+
experiments[experiment.key] = {
92+
id: experiment.id,
93+
key: experiment.key,
94+
variationsMap: (experiment.variations || []).reduce(
95+
(variations: { [key: string]: Variation }, variation) => {
96+
variations[variation.key] = {
97+
id: variation.id,
98+
key: variation.key,
99+
variablesMap: this.getMergedVariablesMap(configObj, variation, experiment.id, featureVariablesMap),
100+
};
101+
if (isFeatureExperiment(configObj, experiment.id)) {
102+
variations[variation.key].featureEnabled = variation.featureEnabled;
103+
}
104+
105+
return variations;
106+
},
107+
{},
108+
),
109+
};
110+
}
111+
112+
return experiments;
113+
},
114+
{},
115+
)
116+
}
117+
118+
/**
119+
* Merge feature key and type from feature variables to variation variables
120+
* @param {ProjectConfig} configObj
121+
* @param {Variation} variation
122+
* @param {string} experimentId
123+
* @param {FeatureVariablesMap} featureVariablesMap
124+
* @returns {OptimizelyVariablesMap} Map of variables
125+
*/
126+
static getMergedVariablesMap(
127+
configObj: ProjectConfig,
128+
variation: Variation,
129+
experimentId: string,
130+
featureVariablesMap: FeatureVariablesMap,
131+
): OptimizelyVariablesMap {
132+
const featureId = configObj.experimentFeatureMap[experimentId];
133+
134+
let variablesObject = {};
135+
if (featureId) {
136+
const experimentFeatureVariables = featureVariablesMap[featureId.toString()];
137+
// Temporary variation variables map to get values to merge.
138+
const tempVariablesIdMap = (variation.variables || []).reduce(
139+
(variablesMap: { [key: string]: VariationVariable }, variable) => {
140+
variablesMap[variable.id] = {
141+
id: variable.id,
142+
value: variable.value,
143+
};
144+
145+
return variablesMap;
146+
},
147+
{},
148+
);
149+
variablesObject = (experimentFeatureVariables || []).reduce(
150+
(variablesMap: OptimizelyVariablesMap, featureVariable) => {
151+
const variationVariable = tempVariablesIdMap[featureVariable.id];
152+
const variableValue =
153+
variation.featureEnabled && variationVariable ? variationVariable.value : featureVariable.defaultValue;
154+
variablesMap[featureVariable.key] = {
155+
id: featureVariable.id,
156+
key: featureVariable.key,
157+
type: featureVariable.type,
158+
value: variableValue,
159+
};
160+
161+
return variablesMap;
162+
},
163+
{},
164+
);
165+
}
166+
167+
return variablesObject;
168+
}
169+
170+
/**
171+
* Get map of all experiments
172+
* @param {ProjectConfig} configObj
173+
* @param {OptimizelyExperimentsMap} allExperiments
174+
* @returns {OptimizelyFeaturesMap} Map of all experiments
175+
*/
176+
static getFeaturesMap(
177+
configObj: ProjectConfig,
178+
allExperiments: OptimizelyExperimentsMap
179+
): OptimizelyFeaturesMap {
180+
return (configObj.featureFlags || []).reduce((features: OptimizelyFeaturesMap, feature) => {
181+
features[feature.key] = {
182+
id: feature.id,
183+
key: feature.key,
184+
experimentsMap: (feature.experimentIds || []).reduce(
185+
(experiments: OptimizelyExperimentsMap, experimentId) => {
186+
const experimentKey = configObj.experimentIdMap[experimentId].key;
187+
experiments[experimentKey] = allExperiments[experimentKey];
188+
return experiments;
189+
},
190+
{},
191+
),
192+
variablesMap: (feature.variables || []).reduce(
193+
(variables: OptimizelyVariablesMap, variable) => {
194+
variables[variable.key] = {
195+
id: variable.id,
196+
key: variable.key,
197+
type: variable.type,
198+
value: variable.defaultValue,
199+
};
200+
201+
return variables;
202+
},
203+
{},
204+
),
205+
};
206+
207+
return features;
208+
}, {});
209+
}
210+
}
211+
212+
/**
213+
* Create an instance of OptimizelyConfig
214+
* @param {ProjectConfig} configObj
215+
* @param {string} datafile
216+
* @returns {OptimizelyConfig} An instance of OptimizelyConfig
217+
*/
218+
export function createOptimizelyConfig(configObj: ProjectConfig, datafile: string): OptimizelyConfig {
219+
return new OptimizelyConfig(configObj, datafile);
220+
}

0 commit comments

Comments
 (0)