Skip to content

Commit 41a2e3e

Browse files
Merge branch 'sdk-configs-rename-split-to-definition' into configs-sdk-client
2 parents b9cb6ed + 7e6e97d commit 41a2e3e

7 files changed

Lines changed: 65 additions & 30 deletions

File tree

src/evaluator/Engine.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ export function engineParser(log: ILogger, split: ISplit, storage: IStorageSync
2929

3030
return {
3131

32-
getTreatment(key: SplitIO.SplitKey | undefined, attributes: SplitIO.Attributes | undefined, splitEvaluator: ISplitEvaluator): MaybeThenable<IEvaluationResult> {
32+
getTreatment(key: SplitIO.SplitKey, attributes: SplitIO.Attributes | undefined, splitEvaluator: ISplitEvaluator): MaybeThenable<IEvaluationResult> {
33+
34+
const parsedKey = keyParser(key);
3335

3436
function evaluate(prerequisitesMet: boolean) {
3537
if (!prerequisitesMet) {
@@ -40,7 +42,7 @@ export function engineParser(log: ILogger, split: ISplit, storage: IStorageSync
4042
};
4143
}
4244

43-
const evaluation = evaluator(key ? keyParser(key) : undefined, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator) as MaybeThenable<IEvaluation>;
45+
const evaluation = evaluator(parsedKey, seed, trafficAllocation, trafficAllocationSeed, attributes, splitEvaluator) as MaybeThenable<IEvaluation>;
4446

4547
return thenable(evaluation) ?
4648
evaluation.then(result => evaluationResult(result, defaultTreatment)) :

src/evaluator/combiners/ifelseif.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function ifElseIfCombinerContext(log: ILogger, predicates: IEvaluator[]):
3333
return undefined;
3434
}
3535

36-
function ifElseIfCombiner(key?: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) {
36+
function ifElseIfCombiner(key: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) {
3737
// In Async environments we are going to have async predicates. There is none way to know
3838
// before hand so we need to evaluate all the predicates, verify for thenables, and finally,
3939
// define how to return the treatment (wrap result into a Promise or not).

src/evaluator/condition/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ function match(log: ILogger, matchingResult: boolean, bucketingKey: string | und
2424
// Condition factory
2525
export function conditionContext(log: ILogger, matcherEvaluator: (key: SplitIO.SplitKeyObject, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable<boolean>, treatments?: { getTreatmentFor: (x: number) => string }, label?: string, conditionType?: 'ROLLOUT' | 'WHITELIST'): IEvaluator {
2626

27-
return function conditionEvaluator(key?: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) {
27+
return function conditionEvaluator(key: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) {
2828

2929
// Whitelisting has more priority than traffic allocation, so we don't apply this filtering to those conditions.
30-
if (!key || (conditionType === 'ROLLOUT' && !shouldApplyRollout(trafficAllocation!, key.bucketingKey, trafficAllocationSeed!))) {
30+
if (conditionType === 'ROLLOUT' && !shouldApplyRollout(trafficAllocation!, key.bucketingKey, trafficAllocationSeed!)) {
3131
return {
3232
treatment: undefined, // treatment value is assigned later
3333
label: NOT_IN_SPLIT

src/evaluator/index.ts

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { engineParser } from './Engine';
22
import { thenable } from '../utils/promise/thenable';
3-
import { EXCEPTION, DEFINITION_NOT_FOUND } from '../utils/labels';
3+
import { EXCEPTION, NO_CONDITION_MATCH, DEFINITION_NOT_FOUND } from '../utils/labels';
44
import { CONTROL } from '../utils/constants';
55
import { ISplit, MaybeThenable } from '../dtos/types';
66
import { IStorageAsync, IStorageSync } from '../storages/types';
@@ -10,24 +10,29 @@ import { ILogger } from '../logger/types';
1010
import { returnSetsUnion, setToArray } from '../utils/lang/sets';
1111
import { WARN_FLAGSET_WITHOUT_FLAGS } from '../logger/constants';
1212

13-
const treatmentException = {
13+
const EVALUATION_EXCEPTION = {
1414
treatment: CONTROL,
1515
label: EXCEPTION,
1616
config: null
1717
};
1818

19+
let EVALUATION_NOT_FOUND = {
20+
treatment: CONTROL,
21+
label: DEFINITION_NOT_FOUND,
22+
config: null
23+
};
24+
1925
function treatmentsException(splitNames: string[]) {
2026
const evaluations: Record<string, IEvaluationResult> = {};
2127
splitNames.forEach(splitName => {
22-
evaluations[splitName] = treatmentException;
28+
evaluations[splitName] = EVALUATION_EXCEPTION;
2329
});
2430
return evaluations;
2531
}
2632

27-
// @TODO: test cases with no key
2833
export function evaluateFeature(
2934
log: ILogger,
30-
key: SplitIO.SplitKey | undefined,
35+
key: SplitIO.SplitKey,
3136
splitName: string,
3237
attributes: SplitIO.Attributes | undefined,
3338
storage: IStorageSync | IStorageAsync,
@@ -39,7 +44,7 @@ export function evaluateFeature(
3944
parsedSplit = storage.splits.getSplit(splitName);
4045
} catch (e) {
4146
// Exception on sync `getSplit` storage. Not possible ATM with InMemory and InLocal storages.
42-
return treatmentException;
47+
return EVALUATION_EXCEPTION;
4348
}
4449

4550
if (thenable(parsedSplit)) {
@@ -53,7 +58,7 @@ export function evaluateFeature(
5358
)).catch(
5459
// Exception on async `getSplit` storage. For example, when the storage is redis or
5560
// pluggable and there is a connection issue and we can't retrieve the split to be evaluated
56-
() => treatmentException
61+
() => EVALUATION_EXCEPTION
5762
);
5863
}
5964

@@ -140,21 +145,16 @@ export function evaluateFeaturesByFlagSets(
140145

141146
function getEvaluation(
142147
log: ILogger,
143-
key: SplitIO.SplitKey | undefined,
148+
key: SplitIO.SplitKey,
144149
splitJSON: ISplit | null,
145150
attributes: SplitIO.Attributes | undefined,
146151
storage: IStorageSync | IStorageAsync,
147152
options?: SplitIO.EvaluationOptions,
148153
): MaybeThenable<IEvaluationResult> {
149-
let evaluation: MaybeThenable<IEvaluationResult> = {
150-
treatment: CONTROL,
151-
label: DEFINITION_NOT_FOUND,
152-
config: null
153-
};
154154

155155
if (splitJSON) {
156156
const split = engineParser(log, splitJSON, storage);
157-
evaluation = split.getTreatment(key, attributes, evaluateFeature);
157+
const evaluation = split.getTreatment(key, attributes, evaluateFeature);
158158

159159
// If the storage is async and the evaluated flag uses segments or dependencies, evaluation is thenable
160160
if (thenable(evaluation)) {
@@ -172,9 +172,11 @@ function getEvaluation(
172172
// @ts-expect-error impressionsDisabled is not exposed in the public typings yet.
173173
evaluation.impressionsDisabled = options?.impressionsDisabled || splitJSON.impressionsDisabled;
174174
}
175+
176+
return evaluation;
175177
}
176178

177-
return evaluation;
179+
return EVALUATION_NOT_FOUND;
178180
}
179181

180182
function getEvaluations(
@@ -208,3 +210,35 @@ function getEvaluations(
208210

209211
return thenables.length > 0 ? Promise.all(thenables).then(() => result) : result;
210212
}
213+
214+
export function evaluateDefaultTreatment(
215+
splitName: string,
216+
storage: IStorageSync | IStorageAsync,
217+
): MaybeThenable<IEvaluationResult> {
218+
let parsedSplit;
219+
220+
try {
221+
parsedSplit = storage.splits.getSplit(splitName);
222+
} catch (e) {
223+
return EVALUATION_EXCEPTION;
224+
}
225+
226+
return thenable(parsedSplit) ?
227+
parsedSplit.then(getDefaultTreatment).catch(() => EVALUATION_EXCEPTION) :
228+
getDefaultTreatment(parsedSplit);
229+
}
230+
231+
function getDefaultTreatment(
232+
splitJSON: ISplit | null,
233+
): MaybeThenable<IEvaluationResult> {
234+
if (splitJSON) {
235+
return {
236+
treatment: splitJSON.defaultTreatment,
237+
label: NO_CONDITION_MATCH, // "default rule"
238+
config: splitJSON.configurations && splitJSON.configurations[splitJSON.defaultTreatment] || null,
239+
changeNumber: splitJSON.changeNumber
240+
};
241+
}
242+
243+
return EVALUATION_NOT_FOUND;
244+
}

src/evaluator/matchers/prerequisites.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { IDependencyMatcherValue, ISplitEvaluator } from '../types';
66

77
export function prerequisitesMatcherContext(prerequisites: ISplit['prerequisites'], storage: IStorageSync | IStorageAsync, log: ILogger) {
88

9-
return function prerequisitesMatcher({ key, attributes }: Partial<IDependencyMatcherValue>, splitEvaluator: ISplitEvaluator): MaybeThenable<boolean> {
9+
return function prerequisitesMatcher({ key, attributes }: IDependencyMatcherValue, splitEvaluator: ISplitEvaluator): MaybeThenable<boolean> {
1010

1111
prerequisites = prerequisites == null ? [] : prerequisites;
1212

src/evaluator/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ export interface IEvaluation {
2727

2828
export type IEvaluationResult = IEvaluation & { treatment: string; impressionsDisabled?: boolean }
2929

30-
export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey | undefined, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
30+
export type ISplitEvaluator = (log: ILogger, key: SplitIO.SplitKey, splitName: string, attributes: SplitIO.Attributes | undefined, storage: IStorageSync | IStorageAsync) => MaybeThenable<IEvaluation>
3131

32-
export type IEvaluator = (key?: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable<IEvaluation | boolean | undefined>
32+
export type IEvaluator = (key: SplitIO.SplitKeyObject, seed?: number, trafficAllocation?: number, trafficAllocationSeed?: number, attributes?: SplitIO.Attributes, splitEvaluator?: ISplitEvaluator) => MaybeThenable<IEvaluation | boolean | undefined>
3333

3434
export type IMatcher = (value: string | number | boolean | string[] | IDependencyMatcherValue, splitEvaluator?: ISplitEvaluator) => MaybeThenable<boolean>

src/sdkClient/client.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
3939
const { log, mode } = settings;
4040
const isAsync = isConsumerMode(mode);
4141

42-
function getTreatment(key: SplitIO.SplitKey | undefined, featureFlagName: string, attributes?: SplitIO.Attributes, options?: SplitIO.EvaluationOptions, withConfig = false, methodName = GET_TREATMENT) {
42+
function getTreatment(key: SplitIO.SplitKey, featureFlagName: string, attributes?: SplitIO.Attributes, options?: SplitIO.EvaluationOptions, withConfig = false, methodName = GET_TREATMENT) {
4343
const stopTelemetryTracker = telemetryTracker.trackEval(withConfig ? TREATMENT_WITH_CONFIG : TREATMENT);
4444

4545
const wrapUp = (evaluationResult: IEvaluationResult) => {
@@ -134,12 +134,15 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
134134
function processEvaluation(
135135
evaluation: IEvaluationResult,
136136
featureFlagName: string,
137-
key: SplitIO.SplitKey | undefined,
137+
key: SplitIO.SplitKey,
138138
properties: string | undefined,
139139
withConfig: boolean,
140140
invokingMethodName: string,
141141
queue: ImpressionDecorated[]
142142
): SplitIO.Treatment | Pick<IEvaluation, 'treatment' | 'config'> {
143+
const matchingKey = getMatching(key);
144+
const bucketingKey = getBucketing(key);
145+
143146
const { changeNumber, impressionsDisabled } = evaluation;
144147
let { treatment, label, config = null } = evaluation;
145148

@@ -150,11 +153,7 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl
150153
config = fallbackTreatment.config;
151154
}
152155

153-
// If no target/key, no impression is tracked
154-
if (key && validateDefinitionExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
155-
const matchingKey = getMatching(key);
156-
const bucketingKey = getBucketing(key);
157-
156+
if (validateDefinitionExistence(log, readinessManager, featureFlagName, label, invokingMethodName)) {
158157
log.info(IMPRESSION_QUEUEING, [featureFlagName, matchingKey, treatment, label]);
159158
queue.push({
160159
imp: {

0 commit comments

Comments
 (0)