Skip to content

Commit d8c2388

Browse files
committed
feat: use-evaluation-context-generic-with-node-metadata-implementation
1 parent 778cdc4 commit d8c2388

5 files changed

Lines changed: 73 additions & 63 deletions

File tree

flagsmith-engine/evaluation/evaluationContext/mappers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
Features,
2+
FeaturesWithMetadata,
33
Segments,
44
Traits,
55
EvaluationContext,
@@ -38,7 +38,7 @@ function mapEnvironmentModelToEvaluationContext(environment: EnvironmentModel):
3838
name: environment.project.name
3939
};
4040

41-
const features: Features = {};
41+
const features: FeaturesWithMetadata = {};
4242
for (const fs of environment.featureStates) {
4343
const variants =
4444
fs.multivariateFeatureStateValues?.length > 0

flagsmith-engine/evaluation/models.ts

Lines changed: 36 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,44 @@
22
// All types from evaluations should be at least imported here and re-exported
33
// Do not use types directly from generated files
44

5-
import type {
6-
EnvironmentContext,
7-
IdentityContext,
8-
SegmentContext,
9-
SegmentRule,
10-
SegmentCondition,
11-
InSegmentCondition,
12-
FeatureContext,
13-
FeatureValue as ContextFeatureValue,
14-
Traits,
15-
Features,
16-
Segments
17-
} from './evaluationContext/evaluationContext.types.js';
18-
195
import type {
206
EvaluationResult as EvaluationContextResult,
21-
FlagResult as EvaluationContextResultFlagResult,
7+
FlagResult,
228
Metadata
239
} from './evaluationResult/evaluationResult.types.js';
2410

25-
export type EnvironmentKey = EnvironmentContext['key'];
26-
export type EnvironmentName = EnvironmentContext['name'];
27-
28-
export type IdentityIdentifier = IdentityContext['identifier'];
29-
export type IdentityKey = IdentityContext['key'];
30-
31-
export type SegmentKey = SegmentContext['key'];
32-
export type SegmentName = SegmentContext['name'];
33-
export type SegmentRuleType = SegmentRule['type'];
34-
export type ConditionOperator = SegmentCondition['operator'] | InSegmentCondition['operator'];
35-
export type ConditionProperty = SegmentCondition['property'] | InSegmentCondition['property'];
36-
export type ConditionValue = SegmentCondition['value'] | InSegmentCondition['value'];
37-
38-
export type FeatureKey = FeatureContext['feature_key'];
39-
export type FeatureName = FeatureContext['name'];
40-
export type FeatureEnabled = FeatureContext['enabled'];
41-
export type FeatureValue = FeatureContext['value'];
42-
export type FeaturePriority = FeatureContext['priority'];
43-
export type FeatureVariants = FeatureContext['variants'];
44-
45-
export type VariantValue = ContextFeatureValue['value'];
46-
export type VariantWeight = ContextFeatureValue['weight'];
11+
import type {
12+
EvaluationContext as GeneratedEvaluationContext,
13+
FeatureContext as GeneratedFeatureContext
14+
} from './evaluationContext/evaluationContext.types.js';
4715

48-
export type TraitMap = Traits;
49-
export type FeatureMap = Features;
50-
export type SegmentMap = Segments;
16+
export interface FeatureMetadata extends Metadata {
17+
flagsmithId: number;
18+
}
5119

52-
export type SegmentConditionOperator = SegmentCondition['operator'];
20+
export interface FeatureContext<T extends Metadata = Metadata> {
21+
key: GeneratedFeatureContext['key'];
22+
feature_key: GeneratedFeatureContext['feature_key'];
23+
name: GeneratedFeatureContext['name'];
24+
enabled: GeneratedFeatureContext['enabled'];
25+
value: GeneratedFeatureContext['value'];
26+
variants?: GeneratedFeatureContext['variants'];
27+
priority?: GeneratedFeatureContext['priority'];
28+
metadata?: T;
29+
[k: string]: unknown;
30+
}
5331

54-
export type EvaluationReason = EvaluationContextResultFlagResult['reason'];
32+
export type FeaturesWithMetadata<T extends Metadata = Metadata> = {
33+
[k: string]: FeatureContext<T>;
34+
};
5535

56-
export type EvaluationResultSegments = EvaluationContextResult['segments'];
57-
import type { FlagResult } from './evaluationResult/evaluationResult.types.js';
36+
export interface EvaluationContext<T extends Metadata = Metadata> {
37+
environment: GeneratedEvaluationContext['environment'];
38+
identity?: GeneratedEvaluationContext['identity'];
39+
segments?: GeneratedEvaluationContext['segments'];
40+
features?: FeaturesWithMetadata<T>;
41+
[k: string]: unknown;
42+
}
5843

5944
export type FlagResultWithMetadata<T extends Metadata = Metadata> = FlagResult & {
6045
metadata?: T;
@@ -65,11 +50,16 @@ export type EvaluationResultFlags<T extends Metadata = Metadata> = Record<
6550
FlagResultWithMetadata<T>
6651
>;
6752

68-
export type EvaluationResult = {
69-
flags: EvaluationResultFlags;
53+
export type EvaluationResultSegments = EvaluationContextResult['segments'];
54+
55+
export type EvaluationResult<T extends Metadata = Metadata> = {
56+
flags: EvaluationResultFlags<T>;
7057
segments: EvaluationResultSegments;
7158
};
7259

60+
export type EvaluationResultWithMetadata = EvaluationResult<FeatureMetadata>;
61+
export type EvaluationContextWithMetadata = EvaluationContext<FeatureMetadata>;
62+
7363
export enum SegmentSource {
7464
API = 'api',
7565
IDENTITY_OVERRIDE = 'identity_override'

flagsmith-engine/index.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { EvaluationContext, FeatureContext, SegmentSource } from './evaluation/models.js';
1+
import {
2+
EvaluationContextWithMetadata,
3+
EvaluationResultSegments,
4+
EvaluationResultWithMetadata,
5+
FeatureContext,
6+
FeatureMetadata
7+
} from './evaluation/models.js';
28
import { getIdentitySegments } from './segments/evaluators.js';
3-
import { EvaluationResult, EvaluationResultFlags } from './evaluation/models.js';
9+
import { EvaluationResultFlags } from './evaluation/models.js';
410
import { TARGETING_REASONS } from './features/types.js';
511
import { getHashedPercentageForObjIds } from './utils/hashing/index.js';
612
export { EnvironmentModel } from './environments/models.js';
@@ -11,7 +17,7 @@ export { FeatureModel, FeatureStateModel } from './features/models.js';
1117
export { OrganisationModel } from './organisations/models.js';
1218

1319
type SegmentOverride = {
14-
feature: FeatureContext;
20+
feature: FeatureContext<FeatureMetadata>;
1521
segmentName: string;
1622
};
1723

@@ -27,7 +33,9 @@ export type SegmentOverrides = Record<string, SegmentOverride>;
2733
* @param context - EvaluationContext containing environment, identity, and segment data
2834
* @returns EvaluationResult with flags, segments, and original context
2935
*/
30-
export function getEvaluationResult(context: EvaluationContext): EvaluationResult {
36+
export function getEvaluationResult(
37+
context: EvaluationContextWithMetadata
38+
): EvaluationResultWithMetadata {
3139
const { segments, segmentOverrides } = evaluateSegments(context);
3240
const flags = evaluateFeatures(context, segmentOverrides);
3341

@@ -40,12 +48,15 @@ export function getEvaluationResult(context: EvaluationContext): EvaluationResul
4048
* @param context - EvaluationContext containing identity and segment definitions
4149
* @returns Object containing segments the identity belongs to and any feature overrides
4250
*/
43-
export function evaluateSegments(context: EvaluationContext): {
44-
segments: EvaluationResult['segments'];
51+
export function evaluateSegments(context: EvaluationContextWithMetadata): {
52+
segments: EvaluationResultSegments;
4553
segmentOverrides: Record<string, SegmentOverride>;
4654
} {
4755
if (!context.identity || !context.segments) {
48-
return { segments: [], segmentOverrides: {} };
56+
return {
57+
segments: [],
58+
segmentOverrides: {} as Record<string, SegmentOverride>
59+
};
4960
}
5061
const identitySegments = getIdentitySegments(context);
5162

@@ -108,10 +119,10 @@ export function processSegmentOverrides(identitySegments: any[]): Record<string,
108119
* @returns EvaluationResultFlags containing evaluated flag results
109120
*/
110121
export function evaluateFeatures(
111-
context: EvaluationContext,
122+
context: EvaluationContextWithMetadata,
112123
segmentOverrides: Record<string, SegmentOverride>
113-
): EvaluationResultFlags {
114-
const flags: EvaluationResultFlags = {};
124+
): EvaluationResultFlags<FeatureMetadata> {
125+
const flags: EvaluationResultFlags<FeatureMetadata> = {};
115126

116127
for (const feature of Object.values(context.features || {})) {
117128
const segmentOverride = segmentOverrides[feature.feature_key];

sdk/models.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { EvaluationResult, FlagResultWithMetadata } from '../flagsmith-engine/evaluation/models.js';
1+
import {
2+
EvaluationResult,
3+
FeatureMetadata,
4+
FlagResultWithMetadata
5+
} from '../flagsmith-engine/evaluation/models.js';
26
import { FeatureStateModel } from '../flagsmith-engine/features/models.js';
37
import { AnalyticsProcessor } from './analytics.js';
48

@@ -81,11 +85,11 @@ export class Flag extends BaseFlag {
8185
});
8286
}
8387

84-
static fromFlagResult(flagResult: FlagResultWithMetadata<{ flagsmithId?: number }>): Flag {
88+
static fromFlagResult(flagResult: FlagResultWithMetadata<FeatureMetadata>): Flag {
8589
return new Flag({
8690
enabled: flagResult.enabled,
8791
value: flagResult.value ?? null,
88-
featureId: flagResult.metadata?.flagsmithId || Number(flagResult.feature_key),
92+
featureId: flagResult.metadata?.flagsmithId ?? Number(flagResult.feature_key),
8993
featureName: flagResult.name,
9094
reason: flagResult.reason
9195
});

tests/engine/e2e/engine.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import { getEvaluationResult } from '../../../flagsmith-engine/index.js';
55
import { Flags } from '../../../sdk/models.js';
66
import { EvaluationContext } from '../../../flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.js';
77
import { parse as parseJsonc } from 'jsonc-parser';
8-
import { EvaluationResult } from '../../../flagsmith-engine/evaluation/models.js';
8+
import {
9+
EvaluationContextWithMetadata,
10+
EvaluationResult
11+
} from '../../../flagsmith-engine/evaluation/models.js';
912

1013
const __filename = fileURLToPath(import.meta.url);
1114
const __dirname = path.dirname(__filename);
@@ -39,7 +42,9 @@ describe('Engine Integration Tests', () => {
3942

4043
test(testName, () => {
4144
const testCase = loadTestFile(filePath);
42-
const engine_response = getEvaluationResult(testCase.context);
45+
const engine_response = getEvaluationResult(
46+
testCase.context as EvaluationContextWithMetadata
47+
);
4348
expect(engine_response).toStrictEqual(testCase.result);
4449
});
4550
});

0 commit comments

Comments
 (0)