Skip to content

Commit 146fb43

Browse files
authored
feat: Added a new getAllFeatureVariables Api (#470)
Summary: Implemented getAllFeatureVariables API Test plan: Added new unit tests.
1 parent cb56b12 commit 146fb43

File tree

4 files changed

+633
-45
lines changed

4 files changed

+633
-45
lines changed

packages/optimizely-sdk/lib/index.d.ts

+11
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ declare module '@optimizely/optimizely-sdk' {
8989
userId: string,
9090
attributes?: UserAttributes
9191
): string | null;
92+
getFeatureVariableJson(
93+
featureKey: string,
94+
variableKey: string,
95+
userId: string,
96+
attributes?: UserAttributes
97+
): unknown;
98+
getAllFeatureVariables(
99+
featureKey: string,
100+
userId: string,
101+
attributes?: UserAttributes
102+
): unknown;
92103
getOptimizelyConfig(): OptimizelyConfig | null;
93104
onReady(options?: { timeout?: number }): Promise<{ success: boolean; reason?: string }>;
94105
close(): Promise<{ success: boolean; reason?: string }>;

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

+133-44
Original file line numberDiff line numberDiff line change
@@ -744,33 +744,78 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK
744744
return null;
745745
}
746746

747-
if (!variableType) {
748-
variableType = variable.type;
749-
} else if (variable.type !== variableType) {
747+
if (variableType && variable.type !== variableType) {
750748
this.logger.log(
751749
LOG_LEVEL.WARNING,
752750
sprintf(LOG_MESSAGES.VARIABLE_REQUESTED_WITH_WRONG_TYPE, MODULE_NAME, variableType, variable.type)
753751
);
754752
return null;
755753
}
754+
755+
var decision = this.decisionService.getVariationForFeature(configObj, featureFlag, userId, attributes);
756+
var featureEnabled = decision.variation !== null ? decision.variation.featureEnabled : false;
757+
var variableValue = this._getFeatureVariableValueFromVariation(featureKey, featureEnabled, decision.variation, variable, userId);
756758

757-
var featureEnabled = false;
758-
var variableValue = variable.defaultValue;
759-
var decision = this.decisionService.getVariationForFeature(configObj, featureFlag, userId, attributes);
759+
var sourceInfo = {};
760+
if (decision.decisionSource === DECISION_SOURCES.FEATURE_TEST) {
761+
sourceInfo = {
762+
experimentKey: decision.experiment.key,
763+
variationKey: decision.variation.key,
764+
};
765+
}
766+
767+
this.notificationCenter.sendNotifications(NOTIFICATION_TYPES.DECISION, {
768+
type: DECISION_NOTIFICATION_TYPES.FEATURE_VARIABLE,
769+
userId: userId,
770+
attributes: attributes || {},
771+
decisionInfo: {
772+
featureKey: featureKey,
773+
featureEnabled: featureEnabled,
774+
source: decision.decisionSource,
775+
variableKey: variableKey,
776+
variableValue: variableValue,
777+
variableType: variable.type,
778+
sourceInfo: sourceInfo,
779+
},
780+
});
781+
return variableValue;
782+
};
783+
784+
/**
785+
* Helper method to get the non type-casted value for a variable attached to a
786+
* feature flag. Returns appropriate variable value depending on whether there
787+
* was a matching variation, feature was enabled or not or varible was part of the
788+
* available variation or not. Also logs the appropriate message explaining how it
789+
* evaluated the value of the variable.
790+
*
791+
* @param {string} featureKey Key of the feature whose variable's value is
792+
* being accessed
793+
* @param {boolean} featureEnabled Boolean indicating if feature is enabled or not
794+
* @param {object} variation variation returned by decision service
795+
* @param {object} variable varible whose value is being evaluated
796+
* @param {string} userId ID for the user
797+
* @return {string|null} String value of the variable or null if the config Obj
798+
* is null
799+
*/
800+
Optimizely.prototype._getFeatureVariableValueFromVariation = function(featureKey, featureEnabled, variation, variable, userId) {
801+
var configObj = this.projectConfigManager.getConfig();
802+
if (!configObj) {
803+
return null;
804+
}
760805

761-
if (decision.variation !== null) {
762-
featureEnabled = decision.variation.featureEnabled;
763-
var value = projectConfig.getVariableValueForVariation(configObj, variable, decision.variation, this.logger);
806+
var variableValue = variable.defaultValue;
807+
if (variation !== null) {
808+
var value = projectConfig.getVariableValueForVariation(configObj, variable, variation, this.logger);
764809
if (value !== null) {
765-
if (featureEnabled === true) {
810+
if (featureEnabled) {
766811
variableValue = value;
767812
this.logger.log(
768813
LOG_LEVEL.INFO,
769814
sprintf(
770815
LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE,
771816
MODULE_NAME,
772-
variableKey,
773-
featureFlag.key,
817+
variable.key,
818+
featureKey,
774819
variableValue,
775820
userId
776821
)
@@ -781,9 +826,9 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK
781826
sprintf(
782827
LOG_MESSAGES.FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE,
783828
MODULE_NAME,
784-
featureFlag.key,
829+
featureKey,
785830
userId,
786-
variableKey
831+
variable.key
787832
)
788833
);
789834
}
@@ -793,8 +838,8 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK
793838
sprintf(
794839
LOG_MESSAGES.VARIABLE_NOT_USED_RETURN_DEFAULT_VARIABLE_VALUE,
795840
MODULE_NAME,
796-
variableKey,
797-
decision.variation.key
841+
variable.key,
842+
variation.key
798843
)
799844
);
800845
}
@@ -805,37 +850,14 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK
805850
LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE,
806851
MODULE_NAME,
807852
userId,
808-
variableKey,
809-
featureFlag.key
853+
variable.key,
854+
featureKey
810855
)
811856
);
812857
}
813-
814-
var sourceInfo = {};
815-
if (decision.decisionSource === DECISION_SOURCES.FEATURE_TEST) {
816-
sourceInfo = {
817-
experimentKey: decision.experiment.key,
818-
variationKey: decision.variation.key,
819-
};
820-
}
821-
822-
var typeCastedValue = projectConfig.getTypeCastValue(variableValue, variableType, this.logger);
823-
this.notificationCenter.sendNotifications(NOTIFICATION_TYPES.DECISION, {
824-
type: DECISION_NOTIFICATION_TYPES.FEATURE_VARIABLE,
825-
userId: userId,
826-
attributes: attributes || {},
827-
decisionInfo: {
828-
featureKey: featureKey,
829-
featureEnabled: featureEnabled,
830-
source: decision.decisionSource,
831-
variableKey: variableKey,
832-
variableValue: typeCastedValue,
833-
variableType: variableType,
834-
sourceInfo: sourceInfo,
835-
},
836-
});
837-
return typeCastedValue;
838-
};
858+
859+
return projectConfig.getTypeCastValue(variableValue, variable.type, this.logger);
860+
}
839861

840862
/**
841863
* Returns value for the given boolean variable attached to the given feature
@@ -957,6 +979,73 @@ Optimizely.prototype.getFeatureVariableJson = function(featureKey, variableKey,
957979
}
958980
};
959981

982+
/**
983+
* Returns values for all the variables attached to the given feature
984+
* flag.
985+
* @param {string} featureKey Key of the feature whose variables are being
986+
* accessed
987+
* @param {string} userId ID for the user
988+
* @param {Object} attributes Optional user attributes
989+
* @return {object|null} Object containing all the variables, or null if the
990+
* feature key is invalid
991+
*/
992+
Optimizely.prototype.getAllFeatureVariables = function(featureKey, userId, attributes) {
993+
try {
994+
if (!this.__isValidInstance()) {
995+
this.logger.log(LOG_LEVEL.ERROR, sprintf(LOG_MESSAGES.INVALID_OBJECT, MODULE_NAME, 'getAllFeatureVariables'));
996+
return null;
997+
}
998+
999+
if (!this.__validateInputs({ feature_key: featureKey, user_id: userId }, attributes)) {
1000+
return null;
1001+
}
1002+
1003+
var configObj = this.projectConfigManager.getConfig();
1004+
if (!configObj) {
1005+
return null;
1006+
}
1007+
1008+
var featureFlag = projectConfig.getFeatureFromKey(configObj, featureKey, this.logger);
1009+
if (!featureFlag) {
1010+
return null;
1011+
}
1012+
1013+
var decision = this.decisionService.getVariationForFeature(configObj, featureFlag, userId, attributes);
1014+
var featureEnabled = decision.variation !== null ? decision.variation.featureEnabled : false;
1015+
var allVariables = {};
1016+
1017+
featureFlag.variables.forEach(function (variable) {
1018+
allVariables[variable.key] = this._getFeatureVariableValueFromVariation(featureKey, featureEnabled, decision.variation, variable, userId);
1019+
}.bind(this));
1020+
1021+
var sourceInfo = {};
1022+
if (decision.decisionSource === DECISION_SOURCES.FEATURE_TEST) {
1023+
sourceInfo = {
1024+
experimentKey: decision.experiment.key,
1025+
variationKey: decision.variation.key,
1026+
};
1027+
}
1028+
this.notificationCenter.sendNotifications(NOTIFICATION_TYPES.DECISION, {
1029+
type: DECISION_NOTIFICATION_TYPES.ALL_FEATURE_VARIABLES,
1030+
userId: userId,
1031+
attributes: attributes || {},
1032+
decisionInfo: {
1033+
featureKey: featureKey,
1034+
featureEnabled: featureEnabled,
1035+
source: decision.decisionSource,
1036+
variableValues: allVariables,
1037+
sourceInfo: sourceInfo,
1038+
},
1039+
});
1040+
1041+
return allVariables;
1042+
} catch (e) {
1043+
this.logger.log(LOG_LEVEL.ERROR, e.message);
1044+
this.errorHandler.handleError(e);
1045+
return null;
1046+
}
1047+
};
1048+
9601049
/**
9611050
* Returns OptimizelyConfig object containing experiments and features data
9621051
* @return {Object}

0 commit comments

Comments
 (0)