Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[submodule "tests/engine/engine-tests/engine-test-data"]
path = tests/engine/engine-tests/engine-test-data
url = [email protected]:Flagsmith/engine-test-data.git
branch = v2.5.0
branch = feat/remove-feature-key-fields
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
*/

/**
* An environment's unique identifier.
* Unique environment key. May be used for selecting a value for a multivariate feature, or for % split segmentation.
*/
export type Key = string;
/**
* An environment's human-readable name.
*/
export type Name = string;
/**
* A unique identifier for an identity, used for segment and multivariate feature flag targeting, and displayed in the Flagsmith UI.
* A unique identifier for an identity as displayed in the Flagsmith UI.
*/
export type Identifier = string;
/**
* Key used when selecting a value for a multivariate feature, or for % split segmentation. Set to an internal identifier or a composite value based on the environment key and identifier, depending on Flagsmith implementation.
*/
export type Key1 = string;
/**
* Key used for % split segmentation.
* Unique segment key used for % split segmentation.
*/
export type Key2 = string;
/**
Expand Down Expand Up @@ -85,13 +85,9 @@ export type SubRules = SegmentRule[];
*/
export type Rules = SegmentRule[];
/**
* Key used when selecting a value for a multivariate feature. Set to an internal identifier or a UUID, depending on Flagsmith implementation.
* Unique feature key used when selecting a variant if the feature is multivariate. Set to an internal identifier or a UUID, depending on Flagsmith implementation.
*/
export type Key3 = string;
/**
* Unique feature identifier.
*/
export type FeatureKey = string;
/**
* Feature name.
*/
Expand Down Expand Up @@ -155,7 +151,7 @@ export interface EnvironmentContext {
*/
export interface IdentityContext {
identifier: Identifier;
key: Key1;
key?: Key1;
traits?: Traits;
[k: string]: unknown;
}
Expand Down Expand Up @@ -214,7 +210,6 @@ export interface InSegmentCondition {
*/
export interface FeatureContext {
key: Key3;
feature_key: FeatureKey;
name: Name2;
enabled: Enabled;
value: Value2;
Expand Down
12 changes: 6 additions & 6 deletions flagsmith-engine/evaluation/evaluationContext/mappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,13 @@ function mapEnvironmentModelToEvaluationContext(

features[fs.feature.name] = {
key: fs.djangoID?.toString() || fs.featurestateUUID,
feature_key: fs.feature.id.toString(),
name: fs.feature.name,
enabled: fs.enabled,
value: fs.getValue(),
variants,
priority: fs.featureSegment?.priority,
metadata: {
flagsmithId: fs.feature.id
flagsmith_id: fs.feature.id
}
};
}
Expand All @@ -75,11 +74,13 @@ function mapEnvironmentModelToEvaluationContext(
segment.featureStates.length > 0
? segment.featureStates.map(fs => ({
key: fs.djangoID?.toString() || fs.featurestateUUID,
feature_key: fs.feature.id.toString(),
name: fs.feature.name,
enabled: fs.enabled,
value: fs.getValue(),
priority: fs.featureSegment?.priority
priority: fs.featureSegment?.priority,
metadata: {
flagsmith_id: fs.feature.id
}
}))
: [],
metadata: {
Expand Down Expand Up @@ -147,13 +148,12 @@ function mapIdentityOverridesToSegments(identityOverrides: IdentityModel[]): Seg
a.feature.name.localeCompare(b.feature.name)
);
const overridesKey = sortedFeatures.map(fs => ({
feature_key: fs.feature.id.toString(),
name: fs.feature.name,
enabled: fs.enabled,
value: fs.getValue(),
priority: -Infinity,
metadata: {
flagsmithId: fs.feature.id
flagsmith_id: fs.feature.id
}
}));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
* and run json-schema-to-typescript to regenerate this file.
*/

/**
* Unique feature identifier.
*/
export type FeatureKey = string;
/**
* Feature name.
*/
Expand All @@ -25,10 +21,6 @@ export type Value = string | number | boolean | null;
* Reason for the feature flag evaluation.
*/
export type Reason = string;
/**
* Unique segment identifier.
*/
export type Key = string;
/**
* Segment name.
*/
Expand All @@ -53,7 +45,6 @@ export interface Flags {
[k: string]: FlagResult;
}
export interface FlagResult {
feature_key: FeatureKey;
name: Name;
enabled: Enabled;
value: Value;
Expand All @@ -68,7 +59,6 @@ export interface FeatureMetadata {
[k: string]: unknown;
}
export interface SegmentResult {
key: Key;
name: Name1;
metadata?: SegmentMetadata;
[k: string]: unknown;
Expand Down
40 changes: 34 additions & 6 deletions flagsmith-engine/evaluation/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@
import type {
EvaluationResult as EvaluationContextResult,
FlagResult,
FeatureMetadata
FeatureMetadata,
SegmentMetadata
} from './evaluationResult/evaluationResult.types.js';

import type {
FeatureContext,
EnvironmentContext,
IdentityContext,
Segments
Segments,
SegmentContext
} from './evaluationContext/evaluationContext.types.js';

export interface CustomFeatureMetadata extends FeatureMetadata {
flagsmithId?: number;
flagsmith_id: number;
}

export interface CustomSegmentMetadata extends SegmentMetadata {
flagsmith_id: number;
source?: SegmentSource;
}

export interface FeatureContextWithMetadata<T extends FeatureMetadata = FeatureMetadata>
Expand All @@ -29,10 +36,23 @@ export type FeaturesWithMetadata<T extends FeatureMetadata = FeatureMetadata> =
[k: string]: FeatureContextWithMetadata<T>;
};

export interface GenericEvaluationContext<T extends FeatureMetadata = FeatureMetadata> {
export interface SegmentContextWithMetadata<T extends SegmentMetadata = SegmentMetadata>
extends SegmentContext {
metadata: T;
overrides?: FeatureContextWithMetadata<FeatureMetadata>[];
}

export type SegmentsWithMetadata<T extends SegmentMetadata = SegmentMetadata> = {
[k: string]: SegmentContextWithMetadata<T>;
};

export interface GenericEvaluationContext<
T extends FeatureMetadata = FeatureMetadata,
S extends SegmentMetadata = SegmentMetadata
> {
environment: EnvironmentContext;
identity?: IdentityContext | null;
segments?: Segments;
segments?: SegmentsWithMetadata<S>;
features?: FeaturesWithMetadata<T>;
[k: string]: unknown;
}
Expand All @@ -54,7 +74,15 @@ export type EvaluationResult<T extends FeatureMetadata = FeatureMetadata> = {
};

export type EvaluationResultWithMetadata = EvaluationResult<CustomFeatureMetadata>;
export type EvaluationContextWithMetadata = GenericEvaluationContext<CustomFeatureMetadata>;
export type EvaluationContextWithMetadata = GenericEvaluationContext<
CustomFeatureMetadata,
CustomSegmentMetadata
>;

export interface SegmentResultWithMetadata {
name: string;
metadata: CustomSegmentMetadata;
}

export enum SegmentSource {
API = 'api',
Expand Down
8 changes: 3 additions & 5 deletions flagsmith-engine/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export function evaluateSegments(context: EvaluationContextWithMetadata): {
const identitySegments = getIdentitySegments(context);

const segments = identitySegments.map(segment => ({
key: segment.key,
name: segment.name,
...(segment.metadata
? {
Expand Down Expand Up @@ -96,7 +95,7 @@ export function processSegmentOverrides(identitySegments: any[]): Record<string,

for (const override of overridesList) {
if (shouldApplyOverride(override, segmentOverrides)) {
segmentOverrides[override.feature_key] = {
segmentOverrides[override.name] = {
feature: override,
segmentName: segment.name
};
Expand Down Expand Up @@ -126,7 +125,7 @@ export function evaluateFeatures(
const flags: EvaluationResultFlags<CustomFeatureMetadata> = {};

for (const feature of Object.values(context.features || {})) {
const segmentOverride = segmentOverrides[feature.feature_key];
const segmentOverride = segmentOverrides[feature.name];
const finalFeature = segmentOverride ? segmentOverride.feature : feature;
const hasOverride = !!segmentOverride;

Expand All @@ -135,7 +134,6 @@ export function evaluateFeatures(
: evaluateFeatureValue(finalFeature, context.identity?.key);

flags[finalFeature.name] = {
feature_key: finalFeature.feature_key,
name: finalFeature.name,
enabled: finalFeature.enabled,
value: evaluatedValue,
Expand Down Expand Up @@ -198,7 +196,7 @@ export function shouldApplyOverride(
override: any,
existingOverrides: Record<string, SegmentOverride>
): boolean {
const currentOverride = existingOverrides[override.feature_key];
const currentOverride = existingOverrides[override.name];
return (
!currentOverride || isHigherPriority(override.priority, currentOverride.feature.priority)
);
Expand Down
62 changes: 36 additions & 26 deletions flagsmith-engine/segments/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,13 @@ export class SegmentModel {
if (segmentResult.metadata?.source === SegmentSource.IDENTITY_OVERRIDE) {
continue;
}
const segmentContext = evaluationContext.segments[segmentResult.key];
const flagsmithId = segmentResult.metadata?.flagsmith_id;
if (!flagsmithId) {
continue;
}
const segmentContext = evaluationContext.segments[flagsmithId.toString()];
if (segmentContext) {
const segment = new SegmentModel(parseInt(segmentContext.key), segmentContext.name);
const segment = new SegmentModel(flagsmithId, segmentContext.name);
segment.rules = segmentContext.rules.map(rule => new SegmentRuleModel(rule.type));
segment.featureStates = SegmentModel.createFeatureStatesFromOverrides(
segmentContext.overrides || []
Expand All @@ -240,33 +244,39 @@ export class SegmentModel {

private static createFeatureStatesFromOverrides(overrides: Overrides): FeatureStateModel[] {
if (!overrides) return [];
return overrides.map(override => {
const feature = new FeatureModel(
parseInt(override.feature_key),
override.name,
override.variants?.length && override.variants.length > 0
? CONSTANTS.MULTIVARIATE
: CONSTANTS.STANDARD
);

const featureState = new FeatureStateModel(
feature,
override.enabled,
override.priority || 0
);

if (override.value !== undefined) {
featureState.setValue(override.value);
}
return overrides
.filter(override => {
const flagsmithId = override?.metadata?.flagsmith_id;
return typeof flagsmithId === 'number';
})
.map(override => {
const flagsmithId = override.metadata!.flagsmith_id as number;
const feature = new FeatureModel(
flagsmithId,
override.name,
override.variants?.length && override.variants.length > 0
? CONSTANTS.MULTIVARIATE
: CONSTANTS.STANDARD
);

if (override.variants && override.variants.length > 0) {
featureState.multivariateFeatureStateValues = this.createMultivariateValues(
override.variants
const featureState = new FeatureStateModel(
feature,
override.enabled,
override.priority || 0
);
}

return featureState;
});
if (override.value !== undefined) {
featureState.setValue(override.value);
}

if (override.variants && override.variants.length > 0) {
featureState.multivariateFeatureStateValues = this.createMultivariateValues(
override.variants
);
}

return featureState;
});
}

private static createMultivariateValues(variants: any[]): MultivariateFeatureStateValueModel[] {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@
"deploy": "npm i && npm run build && npm publish",
"deploy:beta": "npm i && npm run build && npm publish --tag beta",
"prepare": "husky install",
"generate-evaluation-result-types": "curl -o evaluation-result.json https://raw.githubusercontent.com/Flagsmith/flagsmith/main/sdk/evaluation-result.json && npx json2ts -i evaluation-result.json -o flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.ts && rm evaluation-result.json",
"generate-evaluation-context-types": "curl -o evaluation-context.json https://raw.githubusercontent.com/Flagsmith/flagsmith/main/sdk/evaluation-context.json && npx json2ts -i evaluation-context.json -o flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.ts && rm evaluation-context.json",
"generate-evaluation-result-types": "curl -o evaluation-result.json https://raw.githubusercontent.com/Flagsmith/flagsmith/chore%2Fevaluation-context0key/sdk/evaluation-result.json && npx json2ts -i evaluation-result.json -o flagsmith-engine/evaluation/evaluationResult/evaluationResult.types.ts && rm evaluation-result.json",
"generate-evaluation-context-types": "curl -o evaluation-context.json https://raw.githubusercontent.com/Flagsmith/flagsmith/chore%2Fevaluation-context0key/sdk/evaluation-context.json && npx json2ts -i evaluation-context.json -o flagsmith-engine/evaluation/evaluationContext/evaluationContext.types.ts && rm evaluation-context.json",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: revert once merged

"generate-engine-types": "npm run generate-evaluation-result-types && npm run generate-evaluation-context-types"
},
"dependencies": {
Expand Down
6 changes: 3 additions & 3 deletions sdk/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@ export class Flags {
): Flags {
const flags: { [key: string]: Flag } = {};
for (const flag of Object.values(evaluationResult.flags)) {
const flagsmithId = flag.metadata?.flagsmithId;
if (!flagsmithId) {
const flagsmith_id = flag.metadata?.flagsmith_id;
if (!flagsmith_id) {
continue;
}

flags[flag.name] = new Flag({
enabled: flag.enabled,
value: flag.value ?? null,
featureId: flagsmithId,
featureId: flagsmith_id,
featureName: flag.name,
reason: flag.reason
});
Expand Down
2 changes: 1 addition & 1 deletion tests/engine/engine-tests/engine-test-data
Submodule engine-test-data updated 152 files
Loading