Skip to content

feat: Added a new getAllFeatureVariables Api #470

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 6, 2020
11 changes: 11 additions & 0 deletions packages/optimizely-sdk/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ declare module '@optimizely/optimizely-sdk' {
userId: string,
attributes?: UserAttributes
): string | null;
getFeatureVariableJson(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good catch updating this! Let's use unknown for the return value of these, instead of any.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

featureKey: string,
variableKey: string,
userId: string,
attributes?: UserAttributes
): unknown;
getAllFeatureVariables(
featureKey: string,
userId: string,
attributes?: UserAttributes
): unknown;
getOptimizelyConfig(): OptimizelyConfig | null;
onReady(options?: { timeout?: number }): Promise<{ success: boolean; reason?: string }>;
close(): Promise<{ success: boolean; reason?: string }>;
Expand Down
177 changes: 133 additions & 44 deletions packages/optimizely-sdk/lib/optimizely/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -744,33 +744,78 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK
return null;
}

if (!variableType) {
variableType = variable.type;
} else if (variable.type !== variableType) {
if (variableType && variable.type !== variableType) {
this.logger.log(
LOG_LEVEL.WARNING,
sprintf(LOG_MESSAGES.VARIABLE_REQUESTED_WITH_WRONG_TYPE, MODULE_NAME, variableType, variable.type)
);
return null;
}

var decision = this.decisionService.getVariationForFeature(configObj, featureFlag, userId, attributes);
var featureEnabled = decision.variation !== null ? decision.variation.featureEnabled : false;
var variableValue = this._getFeatureVariableValueFromVariation(featureKey, featureEnabled, decision.variation, variable, userId);

var featureEnabled = false;
var variableValue = variable.defaultValue;
var decision = this.decisionService.getVariationForFeature(configObj, featureFlag, userId, attributes);
var sourceInfo = {};
if (decision.decisionSource === DECISION_SOURCES.FEATURE_TEST) {
sourceInfo = {
experimentKey: decision.experiment.key,
variationKey: decision.variation.key,
};
}

this.notificationCenter.sendNotifications(NOTIFICATION_TYPES.DECISION, {
type: DECISION_NOTIFICATION_TYPES.FEATURE_VARIABLE,
userId: userId,
attributes: attributes || {},
decisionInfo: {
featureKey: featureKey,
featureEnabled: featureEnabled,
source: decision.decisionSource,
variableKey: variableKey,
variableValue: variableValue,
variableType: variable.type,
sourceInfo: sourceInfo,
},
});
return variableValue;
};

/**
* Helper method to get the non type-casted value for a variable attached to a
* feature flag. Returns appropriate variable value depending on whether there
* was a matching variation, feature was enabled or not or varible was part of the
* available variation or not. Also logs the appropriate message explaining how it
* evaluated the value of the variable.
*
* @param {string} featureKey Key of the feature whose variable's value is
* being accessed
* @param {boolean} featureEnabled Boolean indicating if feature is enabled or not
* @param {object} variation variation returned by decision service
* @param {object} variable varible whose value is being evaluated
* @param {string} userId ID for the user
* @return {string|null} String value of the variable or null if the config Obj
* is null
*/
Optimizely.prototype._getFeatureVariableValueFromVariation = function(featureKey, featureEnabled, variation, variable, userId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a doc comment for _getFeatureVariableValueFromVariation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

var configObj = this.projectConfigManager.getConfig();
if (!configObj) {
return null;
}

if (decision.variation !== null) {
featureEnabled = decision.variation.featureEnabled;
var value = projectConfig.getVariableValueForVariation(configObj, variable, decision.variation, this.logger);
var variableValue = variable.defaultValue;
if (variation !== null) {
var value = projectConfig.getVariableValueForVariation(configObj, variable, variation, this.logger);
if (value !== null) {
if (featureEnabled === true) {
if (featureEnabled) {
variableValue = value;
this.logger.log(
LOG_LEVEL.INFO,
sprintf(
LOG_MESSAGES.USER_RECEIVED_VARIABLE_VALUE,
MODULE_NAME,
variableKey,
featureFlag.key,
variable.key,
featureKey,
variableValue,
userId
)
Expand All @@ -781,9 +826,9 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK
sprintf(
LOG_MESSAGES.FEATURE_NOT_ENABLED_RETURN_DEFAULT_VARIABLE_VALUE,
MODULE_NAME,
featureFlag.key,
featureKey,
userId,
variableKey
variable.key
)
);
}
Expand All @@ -793,8 +838,8 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK
sprintf(
LOG_MESSAGES.VARIABLE_NOT_USED_RETURN_DEFAULT_VARIABLE_VALUE,
MODULE_NAME,
variableKey,
decision.variation.key
variable.key,
variation.key
)
);
}
Expand All @@ -805,37 +850,14 @@ Optimizely.prototype._getFeatureVariableForType = function(featureKey, variableK
LOG_MESSAGES.USER_RECEIVED_DEFAULT_VARIABLE_VALUE,
MODULE_NAME,
userId,
variableKey,
featureFlag.key
variable.key,
featureKey
)
);
}

var sourceInfo = {};
if (decision.decisionSource === DECISION_SOURCES.FEATURE_TEST) {
sourceInfo = {
experimentKey: decision.experiment.key,
variationKey: decision.variation.key,
};
}

var typeCastedValue = projectConfig.getTypeCastValue(variableValue, variableType, this.logger);
this.notificationCenter.sendNotifications(NOTIFICATION_TYPES.DECISION, {
type: DECISION_NOTIFICATION_TYPES.FEATURE_VARIABLE,
userId: userId,
attributes: attributes || {},
decisionInfo: {
featureKey: featureKey,
featureEnabled: featureEnabled,
source: decision.decisionSource,
variableKey: variableKey,
variableValue: typeCastedValue,
variableType: variableType,
sourceInfo: sourceInfo,
},
});
return typeCastedValue;
};

return projectConfig.getTypeCastValue(variableValue, variable.type, this.logger);
}

/**
* Returns value for the given boolean variable attached to the given feature
Expand Down Expand Up @@ -957,6 +979,73 @@ Optimizely.prototype.getFeatureVariableJson = function(featureKey, variableKey,
}
};

/**
* Returns values for all the variables attached to the given feature
* flag.
* @param {string} featureKey Key of the feature whose variables are being
* accessed
* @param {string} userId ID for the user
* @param {Object} attributes Optional user attributes
* @return {object|null} Object containing all the variables, or null if the
* feature key is invalid
*/
Optimizely.prototype.getAllFeatureVariables = function(featureKey, userId, attributes) {
try {
if (!this.__isValidInstance()) {
this.logger.log(LOG_LEVEL.ERROR, sprintf(LOG_MESSAGES.INVALID_OBJECT, MODULE_NAME, 'getAllFeatureVariables'));
return null;
}

if (!this.__validateInputs({ feature_key: featureKey, user_id: userId }, attributes)) {
return null;
}

var configObj = this.projectConfigManager.getConfig();
if (!configObj) {
return null;
}

var featureFlag = projectConfig.getFeatureFromKey(configObj, featureKey, this.logger);
if (!featureFlag) {
return null;
}

var decision = this.decisionService.getVariationForFeature(configObj, featureFlag, userId, attributes);
var featureEnabled = decision.variation !== null ? decision.variation.featureEnabled : false;
var allVariables = {};

featureFlag.variables.forEach(function (variable) {
allVariables[variable.key] = this._getFeatureVariableValueFromVariation(featureKey, featureEnabled, decision.variation, variable, userId);
}.bind(this));

var sourceInfo = {};
if (decision.decisionSource === DECISION_SOURCES.FEATURE_TEST) {
sourceInfo = {
experimentKey: decision.experiment.key,
variationKey: decision.variation.key,
};
}
this.notificationCenter.sendNotifications(NOTIFICATION_TYPES.DECISION, {
type: DECISION_NOTIFICATION_TYPES.ALL_FEATURE_VARIABLES,
userId: userId,
attributes: attributes || {},
decisionInfo: {
featureKey: featureKey,
featureEnabled: featureEnabled,
source: decision.decisionSource,
variableValues: allVariables,
sourceInfo: sourceInfo,
},
});

return allVariables;
} catch (e) {
this.logger.log(LOG_LEVEL.ERROR, e.message);
this.errorHandler.handleError(e);
return null;
}
};

/**
* Returns OptimizelyConfig object containing experiments and features data
* @return {Object}
Expand Down
Loading