Skip to content

Commit adf18af

Browse files
fix: Support multiple experiments in flag (#664)
* Refactor _getVariationForFeatureExperiment * Clean up * Remove groupId from feature flag object * Add test data * Fix existing tests with new test data added * Add unit test * Add more unit tests * Add remaining tests * Fixed stubbing issue Co-authored-by: Zeeshan Ashraf <[email protected]>
1 parent d9f8e24 commit adf18af

File tree

8 files changed

+646
-93
lines changed

8 files changed

+646
-93
lines changed

packages/optimizely-sdk/lib/core/bucketer/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,5 @@ export const _generateBucketValue = function(bucketingKey: string): number {
232232
export default {
233233
bucket: bucket,
234234
bucketUserIntoExperiment: bucketUserIntoExperiment,
235+
_generateBucketValue: _generateBucketValue,
235236
};

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

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import { sprintf } from'@optimizely/js-sdk-utils';
1717

1818
import fns from '../../utils/fns';
19-
import bucketer from '../bucketer';
19+
import * as bucketer from '../bucketer';
2020
import * as enums from '../../utils/enums';
2121
import projectConfig from '../project_config';
2222
import AudienceEvaluator from '../audience_evaluator';
@@ -469,43 +469,44 @@ DecisionService.prototype.getVariationForFeature = function(configObj, feature,
469469

470470
DecisionService.prototype._getVariationForFeatureExperiment = function(configObj, feature, userId, attributes, options = {}) {
471471
var decideReasons = [];
472-
var experiment = null;
473472
var variationKey = null;
474-
let decisionVariation;
475-
476-
if (feature.hasOwnProperty('groupId')) {
477-
var group = configObj.groupIdMap[feature.groupId];
478-
if (group) {
479-
experiment = this._getExperimentInGroup(configObj, group, userId);
480-
if (experiment && feature.experimentIds.indexOf(experiment.id) !== -1) {
473+
var decisionVariation;
474+
var index;
475+
var variationForFeatureExperiment;
476+
477+
// Check if the feature flag is under an experiment and the the user is bucketed into one of these experiments
478+
if (feature.experimentIds.length > 0) {
479+
// Evaluate each experiment ID and return the first bucketed experiment variation
480+
for (index = 0; index < feature.experimentIds.length; index++) {
481+
var experiment = projectConfig.getExperimentFromId(configObj, feature.experimentIds[index], this.logger);
482+
if (experiment) {
481483
decisionVariation = this.getVariation(configObj, experiment.key, userId, attributes, options);
482484
decideReasons.push(...decisionVariation.reasons);
483485
variationKey = decisionVariation.result;
486+
if (variationKey) {
487+
var variation = experiment.variationKeyMap[variationKey];
488+
variationForFeatureExperiment = {
489+
experiment: experiment,
490+
variation: variation,
491+
decisionSource: DECISION_SOURCES.FEATURE_TEST,
492+
};
493+
494+
return {
495+
result: variationForFeatureExperiment,
496+
reasons: decideReasons,
497+
}
498+
}
484499
}
485500
}
486-
} else if (feature.experimentIds.length > 0) {
487-
// If the feature does not have a group ID, then it can only be associated
488-
// with one experiment, so we look at the first experiment ID only
489-
experiment = projectConfig.getExperimentFromId(configObj, feature.experimentIds[0], this.logger);
490-
if (experiment) {
491-
decisionVariation = this.getVariation(configObj, experiment.key, userId, attributes, options);
492-
decideReasons.push(...decisionVariation.reasons);
493-
variationKey = decisionVariation.result;
494-
}
495501
} else {
496502
var featureHasNoExperimentsMessage = sprintf(LOG_MESSAGES.FEATURE_HAS_NO_EXPERIMENTS, MODULE_NAME, feature.key);
497503
this.logger.log(LOG_LEVEL.DEBUG, featureHasNoExperimentsMessage);
498504
decideReasons.push(featureHasNoExperimentsMessage);
499505
}
500506

501-
var variation = null;
502-
if (variationKey !== null && experiment !== null) {
503-
variation = experiment.variationKeyMap[variationKey];
504-
}
505-
506-
var variationForFeatureExperiment = {
507-
experiment: experiment,
508-
variation: variation,
507+
variationForFeatureExperiment = {
508+
experiment: null,
509+
variation: null,
509510
decisionSource: DECISION_SOURCES.FEATURE_TEST,
510511
};
511512

@@ -515,26 +516,6 @@ DecisionService.prototype._getVariationForFeatureExperiment = function(configObj
515516
};
516517
};
517518

518-
DecisionService.prototype._getExperimentInGroup = function(configObj, group, userId) {
519-
var experimentId = bucketer.bucketUserIntoExperiment(group, userId, userId, this.logger);
520-
if (experimentId) {
521-
this.logger.log(
522-
LOG_LEVEL.INFO,
523-
sprintf(LOG_MESSAGES.USER_BUCKETED_INTO_EXPERIMENT_IN_GROUP, MODULE_NAME, userId, experimentId, group.id)
524-
);
525-
var experiment = projectConfig.getExperimentFromId(configObj, experimentId, this.logger);
526-
if (experiment) {
527-
return experiment;
528-
}
529-
}
530-
531-
this.logger.log(
532-
LOG_LEVEL.INFO,
533-
sprintf(LOG_MESSAGES.USER_NOT_BUCKETED_INTO_ANY_EXPERIMENT_IN_GROUP, MODULE_NAME, userId, group.id)
534-
);
535-
return null;
536-
};
537-
538519
DecisionService.prototype._getVariationForRollout = function(configObj, feature, userId, attributes) {
539520
var decideReasons = [];
540521
var decisionObj = {};

0 commit comments

Comments
 (0)