UNPKG

@splitsoftware/splitio-commons

Version:
180 lines (179 loc) 10.3 kB
import { evaluateFeature, evaluateFeatures, evaluateFeaturesByFlagSets } from '../evaluator'; import { thenable } from '../utils/promise/thenable'; import { getMatching, getBucketing } from '../utils/key'; import { validateSplitExistence } from '../utils/inputValidation/splitExistence'; import { validateTrafficTypeExistence } from '../utils/inputValidation/trafficTypeExistence'; import { SDK_NOT_READY } from '../utils/labels'; import { CONTROL, TREATMENT, TREATMENTS, TREATMENT_WITH_CONFIG, TREATMENTS_WITH_CONFIG, TRACK, TREATMENTS_WITH_CONFIG_BY_FLAGSETS, TREATMENTS_BY_FLAGSETS, TREATMENTS_BY_FLAGSET, TREATMENTS_WITH_CONFIG_BY_FLAGSET, GET_TREATMENTS_WITH_CONFIG, GET_TREATMENTS_BY_FLAG_SETS, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS, GET_TREATMENTS_BY_FLAG_SET, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET, GET_TREATMENT_WITH_CONFIG, GET_TREATMENT, GET_TREATMENTS, TRACK_FN_LABEL } from '../utils/constants'; import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants'; import { isConsumerMode } from '../utils/settingsValidation/mode'; var treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY }; function treatmentsNotReady(featureFlagNames) { var evaluations = {}; featureFlagNames.forEach(function (featureFlagName) { evaluations[featureFlagName] = treatmentNotReady; }); return evaluations; } function stringify(options) { if (options && options.properties) { try { return JSON.stringify(options.properties); } catch ( /* JSON.stringify should never throw with validated options, but handling just in case */_a) { /* JSON.stringify should never throw with validated options, but handling just in case */ } } } /** * Creator of base client with getTreatments and track methods. */ export function clientFactory(params) { var readinessManager = params.sdkReadinessManager.readinessManager, storage = params.storage, settings = params.settings, impressionsTracker = params.impressionsTracker, eventTracker = params.eventTracker, telemetryTracker = params.telemetryTracker; var log = settings.log, mode = settings.mode; var isAsync = isConsumerMode(mode); function getTreatment(key, featureFlagName, attributes, options, withConfig, methodName) { if (withConfig === void 0) { withConfig = false; } if (methodName === void 0) { methodName = GET_TREATMENT; } var stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT); var wrapUp = function (evaluationResult) { var queue = []; var treatment = processEvaluation(evaluationResult, featureFlagName, key, stringify(options), withConfig, methodName, queue); impressionsTracker.track(queue, attributes); stopTelemetryTracker(queue[0] && queue[0].imp.label); return treatment; }; var evaluation = readinessManager.isReady() || readinessManager.isReadyFromCache() ? evaluateFeature(log, key, featureFlagName, attributes, storage) : isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected Promise.resolve(treatmentNotReady) : treatmentNotReady; return thenable(evaluation) ? evaluation.then(function (res) { return wrapUp(res); }) : wrapUp(evaluation); } function getTreatmentWithConfig(key, featureFlagName, attributes, options) { return getTreatment(key, featureFlagName, attributes, options, true, GET_TREATMENT_WITH_CONFIG); } function getTreatments(key, featureFlagNames, attributes, options, withConfig, methodName) { if (withConfig === void 0) { withConfig = false; } if (methodName === void 0) { methodName = GET_TREATMENTS; } var stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENTS_WITH_CONFIG : TREATMENTS); var wrapUp = function (evaluationResults) { var queue = []; var treatments = {}; var properties = stringify(options); Object.keys(evaluationResults).forEach(function (featureFlagName) { treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, properties, withConfig, methodName, queue); }); impressionsTracker.track(queue, attributes); stopTelemetryTracker(queue[0] && queue[0].imp.label); return treatments; }; var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ? evaluateFeatures(log, key, featureFlagNames, attributes, storage) : isAsync ? // If the SDK is not ready, treatment may be incorrect due to having splits but not segments data, or storage is not connected Promise.resolve(treatmentsNotReady(featureFlagNames)) : treatmentsNotReady(featureFlagNames); return thenable(evaluations) ? evaluations.then(function (res) { return wrapUp(res); }) : wrapUp(evaluations); } function getTreatmentsWithConfig(key, featureFlagNames, attributes, options) { return getTreatments(key, featureFlagNames, attributes, options, true, GET_TREATMENTS_WITH_CONFIG); } function getTreatmentsByFlagSets(key, flagSetNames, attributes, options, withConfig, method, methodName) { if (withConfig === void 0) { withConfig = false; } if (method === void 0) { method = TREATMENTS_BY_FLAGSETS; } if (methodName === void 0) { methodName = GET_TREATMENTS_BY_FLAG_SETS; } var stopTelemetryTracker = telemetryTracker.trackEval(method); var wrapUp = function (evaluationResults) { var queue = []; var treatments = {}; var properties = stringify(options); Object.keys(evaluationResults).forEach(function (featureFlagName) { treatments[featureFlagName] = processEvaluation(evaluationResults[featureFlagName], featureFlagName, key, properties, withConfig, methodName, queue); }); impressionsTracker.track(queue, attributes); stopTelemetryTracker(queue[0] && queue[0].imp.label); return treatments; }; var evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ? evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage, methodName) : isAsync ? Promise.resolve({}) : {}; return thenable(evaluations) ? evaluations.then(function (res) { return wrapUp(res); }) : wrapUp(evaluations); } function getTreatmentsWithConfigByFlagSets(key, flagSetNames, attributes, options) { return getTreatmentsByFlagSets(key, flagSetNames, attributes, options, true, TREATMENTS_WITH_CONFIG_BY_FLAGSETS, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SETS); } function getTreatmentsByFlagSet(key, flagSetName, attributes, options) { return getTreatmentsByFlagSets(key, [flagSetName], attributes, options, false, TREATMENTS_BY_FLAGSET, GET_TREATMENTS_BY_FLAG_SET); } function getTreatmentsWithConfigByFlagSet(key, flagSetName, attributes, options) { return getTreatmentsByFlagSets(key, [flagSetName], attributes, options, true, TREATMENTS_WITH_CONFIG_BY_FLAGSET, GET_TREATMENTS_WITH_CONFIG_BY_FLAG_SET); } // Internal function function processEvaluation(evaluation, featureFlagName, key, properties, withConfig, invokingMethodName, queue) { var matchingKey = getMatching(key); var bucketingKey = getBucketing(key); var treatment = evaluation.treatment, label = evaluation.label, changeNumber = evaluation.changeNumber, _a = evaluation.config, config = _a === void 0 ? null : _a, impressionsDisabled = evaluation.impressionsDisabled; log.info(IMPRESSION, [featureFlagName, matchingKey, treatment, label]); if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) { log.info(IMPRESSION_QUEUEING); queue.push({ imp: { feature: featureFlagName, keyName: matchingKey, treatment: treatment, time: Date.now(), bucketingKey: bucketingKey, label: label, changeNumber: changeNumber, properties: properties }, disabled: impressionsDisabled }); } if (withConfig) { return { treatment: treatment, config: config }; } return treatment; } function track(key, trafficTypeName, eventTypeId, value, properties, size) { if (size === void 0) { size = 1024; } var stopTelemetryTracker = telemetryTracker.trackEval(TRACK); var matchingKey = getMatching(key); var timestamp = Date.now(); var eventData = { eventTypeId: eventTypeId, trafficTypeName: trafficTypeName, value: value, timestamp: timestamp, key: matchingKey, properties: properties }; // This may be async but we only warn, we don't actually care if it is valid or not in terms of queueing the event. validateTrafficTypeExistence(log, readinessManager, storage.splits, mode, trafficTypeName, TRACK_FN_LABEL); var result = eventTracker.track(eventData, size); if (thenable(result)) { return result.then(function (result) { stopTelemetryTracker(); return result; }); } else { stopTelemetryTracker(); return result; } } return { getTreatment: getTreatment, getTreatmentWithConfig: getTreatmentWithConfig, getTreatments: getTreatments, getTreatmentsWithConfig: getTreatmentsWithConfig, getTreatmentsByFlagSets: getTreatmentsByFlagSets, getTreatmentsWithConfigByFlagSets: getTreatmentsWithConfigByFlagSets, getTreatmentsByFlagSet: getTreatmentsByFlagSet, getTreatmentsWithConfigByFlagSet: getTreatmentsWithConfigByFlagSet, track: track, }; }