@splitsoftware/splitio-commons
Version:
Split JavaScript SDK common components
155 lines (154 loc) • 6.72 kB
JavaScript
import { engineParser } from './Engine';
import { thenable } from '../utils/promise/thenable';
import { EXCEPTION, NO_CONDITION_MATCH, DEFINITION_NOT_FOUND } from '../utils/labels';
import { CONTROL } from '../utils/constants';
import { returnSetsUnion, setToArray } from '../utils/lang/sets';
import { WARN_FLAGSET_WITHOUT_FLAGS } from '../logger/constants';
var EVALUATION_EXCEPTION = {
treatment: CONTROL,
label: EXCEPTION,
config: null
};
var EVALUATION_DEFINITION_NOT_FOUND = {
treatment: CONTROL,
label: DEFINITION_NOT_FOUND,
config: null
};
function treatmentsException(definitionNames) {
var evaluations = {};
definitionNames.forEach(function (definitionName) {
evaluations[definitionName] = EVALUATION_EXCEPTION;
});
return evaluations;
}
export function evaluateFeature(log, key, definitionName, attributes, storage, options) {
var definition;
try {
definition = storage.definitions.get(definitionName);
}
catch (e) {
// Exception on sync storage. Not possible ATM with InMemory and InLocal storages.
return EVALUATION_EXCEPTION;
}
if (thenable(definition)) {
return definition.then(function (definition) { return getEvaluation(log, key, definition, attributes, storage, options); }).catch(
// Exception on async storage. For example, when the storage is redis or
// pluggable and there is a connection issue and we can't retrieve the split to be evaluated
function () { return EVALUATION_EXCEPTION; });
}
return getEvaluation(log, key, definition, attributes, storage, options);
}
export function evaluateFeatures(log, key, definitionNames, attributes, storage, options) {
var definitions;
try {
definitions = storage.definitions.getMany(definitionNames);
}
catch (e) {
// Exception on sync storage. Not possible ATM with InMemory and InLocal storages.
return treatmentsException(definitionNames);
}
return thenable(definitions) ?
definitions.then(function (definitions) { return getEvaluations(log, key, definitionNames, definitions, attributes, storage, options); })
.catch(function () {
// Exception on async storage. For example, when the storage is redis or
// pluggable and there is a connection issue and we can't retrieve the split to be evaluated
return treatmentsException(definitionNames);
}) :
getEvaluations(log, key, definitionNames, definitions, attributes, storage, options);
}
export function evaluateFeaturesByFlagSets(log, key, flagSets, attributes, storage, method, options) {
var storedFlagNames;
function evaluate(featureFlagsByFlagSets) {
var featureFlags = new Set();
for (var i = 0; i < flagSets.length; i++) {
var featureFlagByFlagSet = featureFlagsByFlagSets[i];
if (featureFlagByFlagSet.size) {
featureFlags = returnSetsUnion(featureFlags, featureFlagByFlagSet);
}
else {
log.warn(WARN_FLAGSET_WITHOUT_FLAGS, [method, flagSets[i]]);
}
}
return featureFlags.size ?
evaluateFeatures(log, key, setToArray(featureFlags), attributes, storage, options) :
{};
}
// get features by flag sets
try {
storedFlagNames = storage.definitions.getNamesBySets(flagSets);
}
catch (e) {
// return empty evaluations
return {};
}
// evaluate related features
return thenable(storedFlagNames) ?
storedFlagNames.then(function (storedFlagNames) { return evaluate(storedFlagNames); })
.catch(function () {
return {};
}) :
evaluate(storedFlagNames);
}
function getEvaluation(log, key, definition, attributes, storage, options) {
if (definition) {
var split = engineParser(log, definition, storage);
var evaluation = split.getTreatment(key, attributes, evaluateFeature);
// If the storage is async and the evaluated definition uses segments or dependencies, evaluation is thenable
if (thenable(evaluation)) {
return evaluation.then(function (result) {
result.changeNumber = definition.changeNumber;
result.config = definition.configurations && definition.configurations[result.treatment] || null;
// @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
result.impressionsDisabled = (options === null || options === void 0 ? void 0 : options.impressionsDisabled) || definition.impressionsDisabled;
return result;
});
}
else {
evaluation.changeNumber = definition.changeNumber;
evaluation.config = definition.configurations && definition.configurations[evaluation.treatment] || null;
// @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
evaluation.impressionsDisabled = (options === null || options === void 0 ? void 0 : options.impressionsDisabled) || definition.impressionsDisabled;
}
return evaluation;
}
return EVALUATION_DEFINITION_NOT_FOUND;
}
function getEvaluations(log, key, definitionNames, definitions, attributes, storage, options) {
var result = {};
var thenables = [];
definitionNames.forEach(function (definitionName) {
var evaluation = getEvaluation(log, key, definitions[definitionName], attributes, storage, options);
if (thenable(evaluation)) {
thenables.push(evaluation.then(function (res) {
result[definitionName] = res;
}));
}
else {
result[definitionName] = evaluation;
}
});
return thenables.length > 0 ? Promise.all(thenables).then(function () { return result; }) : result;
}
export function evaluateDefaultTreatment(definitionName, storage) {
var definition;
try {
definition = storage.definitions.get(definitionName);
}
catch (e) {
return EVALUATION_EXCEPTION;
}
return thenable(definition) ?
definition.then(getDefaultTreatment).catch(function () { return EVALUATION_EXCEPTION; }) :
getDefaultTreatment(definition);
}
function getDefaultTreatment(definition) {
if (definition) {
return {
treatment: definition.defaultTreatment,
label: NO_CONDITION_MATCH,
config: definition.configurations && definition.configurations[definition.defaultTreatment] || null,
changeNumber: definition.changeNumber
};
}
return EVALUATION_DEFINITION_NOT_FOUND;
}