diff --git a/src/evaluator/Engine.ts b/src/evaluator/Engine.ts index 4228316f..c6e8b3a5 100644 --- a/src/evaluator/Engine.ts +++ b/src/evaluator/Engine.ts @@ -29,9 +29,7 @@ export function engineParser(log: ILogger, split: ISplit, storage: IStorageSync return { - getTreatment(key: SplitIO.SplitKey, attributes: SplitIO.Attributes | undefined, splitEvaluator: ISplitEvaluator): MaybeThenable { - - const parsedKey = keyParser(key); + getTreatment(key: SplitIO.SplitKey | undefined, attributes: SplitIO.Attributes | undefined, splitEvaluator: ISplitEvaluator): MaybeThenable { function evaluate(prerequisitesMet: boolean) { if (!prerequisitesMet) { @@ -42,7 +40,7 @@ export function engineParser(log: ILogger, split: ISplit, storage: IStorageSync }; } - const evaluation = evaluator(parsedKey, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator) as MaybeThenable; + const evaluation = evaluator(key ? keyParser(key) : undefined, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator) as MaybeThenable; return thenable(evaluation) ? evaluation.then(result => evaluationResult(result, defaultTreatment)) : diff --git a/src/evaluator/combiners/ifelseif.ts b/src/evaluator/combiners/ifelseif.ts index aaba4b27..8f7a493d 100644 --- a/src/evaluator/combiners/ifelseif.ts +++ b/src/evaluator/combiners/ifelseif.ts @@ -33,7 +33,7 @@ export function ifElseIfCombinerContext(log: ILogger, predicates: IEvaluator[]): return undefined; } - function ifElseIfCombiner(key: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) { + function ifElseIfCombiner(key?: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) { // In Async environments we are going to have async predicates. There is none way to know // before hand so we need to evaluate all the predicates, verify for thenables, and finally, // define how to return the treatment (wrap result into a Promise or not). diff --git a/src/evaluator/condition/index.ts b/src/evaluator/condition/index.ts index 5facaa5c..c85fc9d2 100644 --- a/src/evaluator/condition/index.ts +++ b/src/evaluator/condition/index.ts @@ -24,10 +24,10 @@ function match(log: ILogger, matchingResult: boolean, bucketingKey: string | und // Condition factory export function conditionContext(log: ILogger, matcherEvaluator: (key: SplitIO.SplitKeyObject, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable, treatments?: { getTreatmentFor: (x: number) => string }, label?: string, conditionType?: 'ROLLOUT' | 'WHITELIST'): IEvaluator { - return function conditionEvaluator(key: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) { + return function conditionEvaluator(key?: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) { // Whitelisting has more priority than traffic allocation, so we don't apply this filtering to those conditions. - if (conditionType === 'ROLLOUT' && !shouldApplyRollout(trafficAllocation!, key.bucketingKey, trafficAllocationSeed!)) { + if (!key || (conditionType === 'ROLLOUT' && !shouldApplyRollout(trafficAllocation!, key.bucketingKey, trafficAllocationSeed!))) { return { treatment: undefined, // treatment value is assigned later label: NOT_IN_SPLIT diff --git a/src/evaluator/index.ts b/src/evaluator/index.ts index 574a8337..b6cb3e97 100644 --- a/src/evaluator/index.ts +++ b/src/evaluator/index.ts @@ -24,9 +24,10 @@ function treatmentsException(splitNames: string[]) { return evaluations; } +// @TODO: test cases with no key export function evaluateFeature( log: ILogger, - key: SplitIO.SplitKey, + key: SplitIO.SplitKey | undefined, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync, @@ -139,7 +140,7 @@ export function evaluateFeaturesByFlagSets( function getEvaluation( log: ILogger, - key: SplitIO.SplitKey, + key: SplitIO.SplitKey | undefined, splitJSON: ISplit | null, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync, diff --git a/src/evaluator/matchers/prerequisites.ts b/src/evaluator/matchers/prerequisites.ts index 247e9948..06ac84cf 100644 --- a/src/evaluator/matchers/prerequisites.ts +++ b/src/evaluator/matchers/prerequisites.ts @@ -6,7 +6,7 @@ import { IDependencyMatcherValue, ISplitEvaluator } from '../types'; export function prerequisitesMatcherContext(prerequisites: ISplit['prerequisites'], storage: IStorageSync | IStorageAsync, log: ILogger) { - return function prerequisitesMatcher({ key, attributes }: IDependencyMatcherValue, splitEvaluator: ISplitEvaluator): MaybeThenable { + return function prerequisitesMatcher({ key, attributes }: Partial, splitEvaluator: ISplitEvaluator): MaybeThenable { prerequisites = prerequisites == null ? [] : prerequisites; diff --git a/src/evaluator/types.ts b/src/evaluator/types.ts index 42900f06..bd89dbd3 100644 --- a/src/evaluator/types.ts +++ b/src/evaluator/types.ts @@ -27,8 +27,8 @@ export interface IEvaluation { export type IEvaluationResult = IEvaluation & { treatment: string; impressionsDisabled?: boolean } -export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable +export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey | undefined, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable -export type IEvaluator = (key: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable +export type IEvaluator = (key?: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable export type IMatcher = (value: string | number | boolean | string[] | IDependencyMatcherValue, splitEvaluator?: ISplitEvaluator) => MaybeThenable diff --git a/src/sdkClient/client.ts b/src/sdkClient/client.ts index 6eded6c3..7c54a822 100644 --- a/src/sdkClient/client.ts +++ b/src/sdkClient/client.ts @@ -39,7 +39,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl const { log, mode } = settings; const isAsync = isConsumerMode(mode); - function getTreatment(key: SplitIO.SplitKey, featureFlagName: string, attributes?: SplitIO.Attributes, options?: SplitIO.EvaluationOptions, withConfig = false, methodName = GET_TREATMENT) { + function getTreatment(key: SplitIO.SplitKey | undefined, featureFlagName: string, attributes?: SplitIO.Attributes, options?: SplitIO.EvaluationOptions, withConfig = false, methodName = GET_TREATMENT) { const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT); const wrapUp = (evaluationResult: IEvaluationResult) => { @@ -134,15 +134,12 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl function processEvaluation( evaluation: IEvaluationResult, featureFlagName: string, - key: SplitIO.SplitKey, + key: SplitIO.SplitKey | undefined, properties: string | undefined, withConfig: boolean, invokingMethodName: string, queue: ImpressionDecorated[] ): SplitIO.Treatment | SplitIO.TreatmentWithConfig { - const matchingKey = getMatching(key); - const bucketingKey = getBucketing(key); - const { changeNumber, impressionsDisabled } = evaluation; let { treatment, label, config = null } = evaluation; @@ -153,7 +150,11 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl config = fallbackTreatment.config; } - if (validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) { + // If no target/key, no impression is tracked + if (key && validateSplitExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) { + const matchingKey = getMatching(key); + const bucketingKey = getBucketing(key); + log.info(IMPRESSION_QUEUEING, [featureFlagName, matchingKey, treatment, label]); queue.push({ imp: {