mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-14 01:16:17 +02:00
115 lines
2.9 KiB
TypeScript
115 lines
2.9 KiB
TypeScript
import { Context } from './context';
|
|
// eslint-disable-next-line import/no-cycle
|
|
import { FeatureInterface } from './feature';
|
|
import normalizedValue from './strategy/util';
|
|
import { resolveContextValue } from './helpers';
|
|
import { PayloadType } from 'unleash-client';
|
|
|
|
interface Override {
|
|
contextName: string;
|
|
values: string[];
|
|
}
|
|
|
|
export interface Payload {
|
|
type: PayloadType;
|
|
value: string;
|
|
}
|
|
|
|
export interface VariantDefinition {
|
|
name: string;
|
|
weight: number;
|
|
stickiness?: string;
|
|
payload: Payload;
|
|
overrides: Override[];
|
|
}
|
|
|
|
export interface Variant {
|
|
name: string;
|
|
enabled: boolean;
|
|
payload?: Payload;
|
|
}
|
|
|
|
export function getDefaultVariant(): Variant {
|
|
return {
|
|
name: 'disabled',
|
|
enabled: false,
|
|
};
|
|
}
|
|
|
|
function randomString() {
|
|
return String(Math.round(Math.random() * 100000));
|
|
}
|
|
|
|
const stickinessSelectors = ['userId', 'sessionId', 'remoteAddress'];
|
|
function getSeed(context: Context, stickiness: string = 'default'): string {
|
|
if (stickiness !== 'default') {
|
|
const value = resolveContextValue(context, stickiness);
|
|
return value ? value.toString() : randomString();
|
|
}
|
|
let result;
|
|
stickinessSelectors.some((key: string): boolean => {
|
|
const value = context[key];
|
|
if (typeof value === 'string' && value !== '') {
|
|
result = value;
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
return result || randomString();
|
|
}
|
|
|
|
function overrideMatchesContext(context: Context): (o: Override) => boolean {
|
|
return (o: Override) =>
|
|
o.values.some(
|
|
(value) => value === resolveContextValue(context, o.contextName),
|
|
);
|
|
}
|
|
|
|
function findOverride(
|
|
feature: FeatureInterface,
|
|
context: Context,
|
|
): VariantDefinition | undefined {
|
|
return feature.variants
|
|
.filter((variant) => variant.overrides)
|
|
.find((variant) =>
|
|
variant.overrides.some(overrideMatchesContext(context)),
|
|
);
|
|
}
|
|
|
|
export function selectVariant(
|
|
feature: FeatureInterface,
|
|
context: Context,
|
|
): VariantDefinition | null {
|
|
const totalWeight = feature.variants.reduce((acc, v) => acc + v.weight, 0);
|
|
if (totalWeight <= 0) {
|
|
return null;
|
|
}
|
|
const variantOverride = findOverride(feature, context);
|
|
if (variantOverride) {
|
|
return variantOverride;
|
|
}
|
|
|
|
const { stickiness } = feature.variants[0];
|
|
|
|
const target = normalizedValue(
|
|
getSeed(context, stickiness),
|
|
feature.name,
|
|
totalWeight,
|
|
);
|
|
|
|
let counter = 0;
|
|
const variant = feature.variants.find(
|
|
(v: VariantDefinition): VariantDefinition | undefined => {
|
|
if (v.weight === 0) {
|
|
return undefined;
|
|
}
|
|
counter += v.weight;
|
|
if (counter < target) {
|
|
return undefined;
|
|
}
|
|
return v;
|
|
},
|
|
);
|
|
return variant || null;
|
|
}
|