Skip to content
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