mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: strategy variants in playground (#4281)
This commit is contained in:
		
							parent
							
								
									ce70f9f54e
								
							
						
					
					
						commit
						f1d1d7d49a
					
				@ -162,7 +162,7 @@
 | 
			
		||||
    "stoppable": "^1.1.0",
 | 
			
		||||
    "ts-toolbelt": "^9.6.0",
 | 
			
		||||
    "type-is": "^1.6.18",
 | 
			
		||||
    "unleash-client": "3.21.0",
 | 
			
		||||
    "unleash-client": "4.1.0-beta.5",
 | 
			
		||||
    "uuid": "^9.0.0"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,10 @@ import { Strategy } from './strategy';
 | 
			
		||||
import { FeatureInterface } from './feature';
 | 
			
		||||
import { RepositoryInterface } from './repository';
 | 
			
		||||
import {
 | 
			
		||||
    Variant,
 | 
			
		||||
    getDefaultVariant,
 | 
			
		||||
    VariantDefinition,
 | 
			
		||||
    selectVariant,
 | 
			
		||||
    Variant,
 | 
			
		||||
    VariantDefinition,
 | 
			
		||||
} from './variant';
 | 
			
		||||
import { Context } from './context';
 | 
			
		||||
import { SegmentForEvaluation } from './strategy/strategy';
 | 
			
		||||
@ -24,6 +24,8 @@ export type EvaluatedPlaygroundStrategy = Omit<
 | 
			
		||||
 | 
			
		||||
export type FeatureStrategiesEvaluationResult = {
 | 
			
		||||
    result: boolean | typeof playgroundStrategyEvaluation.unknownResult;
 | 
			
		||||
    variant?: Variant;
 | 
			
		||||
    variants?: VariantDefinition[];
 | 
			
		||||
    strategies: EvaluatedPlaygroundStrategy[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -110,30 +112,45 @@ export default class UnleashClient {
 | 
			
		||||
                        ?.map(this.getSegment(this.repository))
 | 
			
		||||
                        .filter(Boolean) ?? [];
 | 
			
		||||
 | 
			
		||||
                const evaluationResult = strategy.isEnabledWithConstraints(
 | 
			
		||||
                    strategySelector.parameters,
 | 
			
		||||
                    context,
 | 
			
		||||
                    strategySelector.constraints,
 | 
			
		||||
                    segments,
 | 
			
		||||
                    strategySelector.disabled,
 | 
			
		||||
                    strategySelector.variants,
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                return {
 | 
			
		||||
                    name: strategySelector.name,
 | 
			
		||||
                    id: strategySelector.id,
 | 
			
		||||
                    title: strategySelector.title,
 | 
			
		||||
                    disabled: strategySelector.disabled || false,
 | 
			
		||||
                    parameters: strategySelector.parameters,
 | 
			
		||||
                    ...strategy.isEnabledWithConstraints(
 | 
			
		||||
                        strategySelector.parameters,
 | 
			
		||||
                        context,
 | 
			
		||||
                        strategySelector.constraints,
 | 
			
		||||
                        segments,
 | 
			
		||||
                        strategySelector.disabled,
 | 
			
		||||
                    ),
 | 
			
		||||
                    ...evaluationResult,
 | 
			
		||||
                };
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Feature evaluation
 | 
			
		||||
        const overallStrategyResult = () => {
 | 
			
		||||
        const overallStrategyResult = (): [
 | 
			
		||||
            boolean | typeof playgroundStrategyEvaluation.unknownResult,
 | 
			
		||||
            VariantDefinition[] | undefined,
 | 
			
		||||
            Variant | undefined | null,
 | 
			
		||||
        ] => {
 | 
			
		||||
            // if at least one strategy is enabled, then the feature is enabled
 | 
			
		||||
            const enabledStrategy = strategies.find(
 | 
			
		||||
                (strategy) => strategy.result.enabled === true,
 | 
			
		||||
            );
 | 
			
		||||
            if (
 | 
			
		||||
                strategies.some((strategy) => strategy.result.enabled === true)
 | 
			
		||||
                enabledStrategy &&
 | 
			
		||||
                enabledStrategy.result.evaluationStatus === 'complete'
 | 
			
		||||
            ) {
 | 
			
		||||
                return true;
 | 
			
		||||
                return [
 | 
			
		||||
                    true,
 | 
			
		||||
                    enabledStrategy.result.variants,
 | 
			
		||||
                    enabledStrategy.result.variant,
 | 
			
		||||
                ];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // if at least one strategy is unknown, then the feature _may_ be enabled
 | 
			
		||||
@ -142,14 +159,21 @@ export default class UnleashClient {
 | 
			
		||||
                    (strategy) => strategy.result.enabled === 'unknown',
 | 
			
		||||
                )
 | 
			
		||||
            ) {
 | 
			
		||||
                return playgroundStrategyEvaluation.unknownResult;
 | 
			
		||||
                return [
 | 
			
		||||
                    playgroundStrategyEvaluation.unknownResult,
 | 
			
		||||
                    undefined,
 | 
			
		||||
                    undefined,
 | 
			
		||||
                ];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
            return [false, undefined, undefined];
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const [result, variants, variant] = overallStrategyResult();
 | 
			
		||||
        const evalResults: FeatureStrategiesEvaluationResult = {
 | 
			
		||||
            result: overallStrategyResult(),
 | 
			
		||||
            result,
 | 
			
		||||
            variant,
 | 
			
		||||
            variants,
 | 
			
		||||
            strategies,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@ -197,8 +221,27 @@ export default class UnleashClient {
 | 
			
		||||
    ): Variant {
 | 
			
		||||
        const fallback = fallbackVariant || getDefaultVariant();
 | 
			
		||||
        const feature = this.repository.getToggle(name);
 | 
			
		||||
 | 
			
		||||
        if (typeof feature === 'undefined') {
 | 
			
		||||
            return fallback;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let enabled = true;
 | 
			
		||||
        if (checkToggle) {
 | 
			
		||||
            const result = this.isFeatureEnabled(feature, context, () =>
 | 
			
		||||
                fallbackVariant ? fallbackVariant.enabled : false,
 | 
			
		||||
            );
 | 
			
		||||
            enabled = result.result === true;
 | 
			
		||||
            const strategyVariant = result.variant;
 | 
			
		||||
            if (enabled && strategyVariant) {
 | 
			
		||||
                return strategyVariant;
 | 
			
		||||
            }
 | 
			
		||||
            if (!enabled) {
 | 
			
		||||
                return fallback;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
            typeof feature === 'undefined' ||
 | 
			
		||||
            !feature.variants ||
 | 
			
		||||
            !Array.isArray(feature.variants) ||
 | 
			
		||||
            feature.variants.length === 0 ||
 | 
			
		||||
@ -207,17 +250,6 @@ export default class UnleashClient {
 | 
			
		||||
            return fallback;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let enabled = true;
 | 
			
		||||
        if (checkToggle) {
 | 
			
		||||
            enabled =
 | 
			
		||||
                this.isFeatureEnabled(feature, context, () =>
 | 
			
		||||
                    fallbackVariant ? fallbackVariant.enabled : false,
 | 
			
		||||
                ).result === true;
 | 
			
		||||
            if (!enabled) {
 | 
			
		||||
                return fallback;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const variant: VariantDefinition | null = selectVariant(
 | 
			
		||||
            feature,
 | 
			
		||||
            context,
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ import { PlaygroundSegmentSchema } from 'lib/openapi/spec/playground-segment-sch
 | 
			
		||||
import { StrategyEvaluationResult } from '../client';
 | 
			
		||||
import { Constraint, operators } from '../constraint';
 | 
			
		||||
import { Context } from '../context';
 | 
			
		||||
import { selectVariantDefinition, VariantDefinition } from '../variant';
 | 
			
		||||
 | 
			
		||||
export type SegmentForEvaluation = {
 | 
			
		||||
    name: string;
 | 
			
		||||
@ -16,6 +17,7 @@ export interface StrategyTransportInterface {
 | 
			
		||||
    disabled?: boolean;
 | 
			
		||||
    parameters: any;
 | 
			
		||||
    constraints: Constraint[];
 | 
			
		||||
    variants?: VariantDefinition[];
 | 
			
		||||
    segments?: number[];
 | 
			
		||||
    id?: string;
 | 
			
		||||
}
 | 
			
		||||
@ -114,11 +116,12 @@ export class Strategy {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isEnabledWithConstraints(
 | 
			
		||||
        parameters: unknown,
 | 
			
		||||
        parameters: Record<string, unknown>,
 | 
			
		||||
        context: Context,
 | 
			
		||||
        constraints: Iterable<Constraint>,
 | 
			
		||||
        segments: Array<SegmentForEvaluation>,
 | 
			
		||||
        disabled?: boolean,
 | 
			
		||||
        variantDefinitions?: VariantDefinition[],
 | 
			
		||||
    ): StrategyEvaluationResult {
 | 
			
		||||
        const constraintResults = this.checkConstraints(context, constraints);
 | 
			
		||||
        const enabledResult = this.isEnabled(parameters, context);
 | 
			
		||||
@ -127,10 +130,27 @@ export class Strategy {
 | 
			
		||||
        const overallResult =
 | 
			
		||||
            constraintResults.result && enabledResult && segmentResults.result;
 | 
			
		||||
 | 
			
		||||
        const variantDefinition = variantDefinitions
 | 
			
		||||
            ? selectVariantDefinition(
 | 
			
		||||
                  parameters.groupId as string,
 | 
			
		||||
                  variantDefinitions,
 | 
			
		||||
                  context,
 | 
			
		||||
              )
 | 
			
		||||
            : undefined;
 | 
			
		||||
        const variant = variantDefinition
 | 
			
		||||
            ? {
 | 
			
		||||
                  name: variantDefinition.name,
 | 
			
		||||
                  enabled: true,
 | 
			
		||||
                  payload: variantDefinition.payload,
 | 
			
		||||
              }
 | 
			
		||||
            : undefined;
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            result: {
 | 
			
		||||
                enabled: disabled ? false : overallResult,
 | 
			
		||||
                evaluationStatus: 'complete',
 | 
			
		||||
                variant,
 | 
			
		||||
                variants: variant ? variantDefinitions : undefined,
 | 
			
		||||
            },
 | 
			
		||||
            constraints: constraintResults.constraints,
 | 
			
		||||
            segments: segmentResults.segments,
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@ import { Context } from './context';
 | 
			
		||||
import { FeatureInterface } from './feature';
 | 
			
		||||
import normalizedValue from './strategy/util';
 | 
			
		||||
import { resolveContextValue } from './helpers';
 | 
			
		||||
import { PayloadType } from 'unleash-client';
 | 
			
		||||
 | 
			
		||||
interface Override {
 | 
			
		||||
    contextName: string;
 | 
			
		||||
@ -11,7 +10,7 @@ interface Override {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Payload {
 | 
			
		||||
    type: PayloadType;
 | 
			
		||||
    type: 'string' | 'csv' | 'json';
 | 
			
		||||
    value: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -19,8 +18,8 @@ export interface VariantDefinition {
 | 
			
		||||
    name: string;
 | 
			
		||||
    weight: number;
 | 
			
		||||
    stickiness?: string;
 | 
			
		||||
    payload: Payload;
 | 
			
		||||
    overrides: Override[];
 | 
			
		||||
    payload?: Payload;
 | 
			
		||||
    overrides?: Override[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Variant {
 | 
			
		||||
@ -66,39 +65,40 @@ function overrideMatchesContext(context: Context): (o: Override) => boolean {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findOverride(
 | 
			
		||||
    feature: FeatureInterface,
 | 
			
		||||
    variants: VariantDefinition[],
 | 
			
		||||
    context: Context,
 | 
			
		||||
): VariantDefinition | undefined {
 | 
			
		||||
    return feature.variants
 | 
			
		||||
    return variants
 | 
			
		||||
        .filter((variant) => variant.overrides)
 | 
			
		||||
        .find((variant) =>
 | 
			
		||||
            variant.overrides.some(overrideMatchesContext(context)),
 | 
			
		||||
            variant.overrides?.some(overrideMatchesContext(context)),
 | 
			
		||||
        );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function selectVariant(
 | 
			
		||||
    feature: FeatureInterface,
 | 
			
		||||
export function selectVariantDefinition(
 | 
			
		||||
    featureName: string,
 | 
			
		||||
    variants: VariantDefinition[],
 | 
			
		||||
    context: Context,
 | 
			
		||||
): VariantDefinition | null {
 | 
			
		||||
    const totalWeight = feature.variants.reduce((acc, v) => acc + v.weight, 0);
 | 
			
		||||
    const totalWeight = variants.reduce((acc, v) => acc + v.weight, 0);
 | 
			
		||||
    if (totalWeight <= 0) {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
    const variantOverride = findOverride(feature, context);
 | 
			
		||||
    const variantOverride = findOverride(variants, context);
 | 
			
		||||
    if (variantOverride) {
 | 
			
		||||
        return variantOverride;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { stickiness } = feature.variants[0];
 | 
			
		||||
    const { stickiness } = variants[0];
 | 
			
		||||
 | 
			
		||||
    const target = normalizedValue(
 | 
			
		||||
        getSeed(context, stickiness),
 | 
			
		||||
        feature.name,
 | 
			
		||||
        featureName,
 | 
			
		||||
        totalWeight,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let counter = 0;
 | 
			
		||||
    const variant = feature.variants.find(
 | 
			
		||||
    const variant = variants.find(
 | 
			
		||||
        (v: VariantDefinition): VariantDefinition | undefined => {
 | 
			
		||||
            if (v.weight === 0) {
 | 
			
		||||
                return undefined;
 | 
			
		||||
@ -112,3 +112,10 @@ export function selectVariant(
 | 
			
		||||
    );
 | 
			
		||||
    return variant || null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function selectVariant(
 | 
			
		||||
    feature: FeatureInterface,
 | 
			
		||||
    context: Context,
 | 
			
		||||
): VariantDefinition | null {
 | 
			
		||||
    return selectVariantDefinition(feature.name, feature.variants, context);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -250,6 +250,77 @@ describe('offline client', () => {
 | 
			
		||||
        expect(client.isEnabled(name, {}).result).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('returns strategy variant over feature variant', async () => {
 | 
			
		||||
        const name = 'toggle-name';
 | 
			
		||||
        const client = await offlineUnleashClient({
 | 
			
		||||
            features: [
 | 
			
		||||
                {
 | 
			
		||||
                    strategies: [
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'default',
 | 
			
		||||
                            constraints: [
 | 
			
		||||
                                {
 | 
			
		||||
                                    values: ['my-app-name'],
 | 
			
		||||
                                    inverted: false,
 | 
			
		||||
                                    operator: 'IN' as 'IN',
 | 
			
		||||
                                    contextName: 'appName',
 | 
			
		||||
                                    caseInsensitive: false,
 | 
			
		||||
                                },
 | 
			
		||||
                            ],
 | 
			
		||||
                            variants: [
 | 
			
		||||
                                {
 | 
			
		||||
                                    name: 'ignoreNonMatchingStrategyVariant',
 | 
			
		||||
                                    weightType: 'variable',
 | 
			
		||||
                                    weight: 1000,
 | 
			
		||||
                                    stickiness: 'default',
 | 
			
		||||
                                },
 | 
			
		||||
                            ],
 | 
			
		||||
                        },
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'default',
 | 
			
		||||
                            constraints: [
 | 
			
		||||
                                {
 | 
			
		||||
                                    values: ['client-test'],
 | 
			
		||||
                                    inverted: false,
 | 
			
		||||
                                    operator: 'IN' as 'IN',
 | 
			
		||||
                                    contextName: 'appName',
 | 
			
		||||
                                    caseInsensitive: false,
 | 
			
		||||
                                },
 | 
			
		||||
                            ],
 | 
			
		||||
                            variants: [
 | 
			
		||||
                                {
 | 
			
		||||
                                    name: 'strategyVariant',
 | 
			
		||||
                                    weightType: 'variable',
 | 
			
		||||
                                    weight: 1000,
 | 
			
		||||
                                    stickiness: 'default',
 | 
			
		||||
                                },
 | 
			
		||||
                            ],
 | 
			
		||||
                        },
 | 
			
		||||
                    ],
 | 
			
		||||
                    project: 'default',
 | 
			
		||||
                    stale: false,
 | 
			
		||||
                    enabled: true,
 | 
			
		||||
                    name,
 | 
			
		||||
                    type: 'experiment',
 | 
			
		||||
                    variants: [
 | 
			
		||||
                        {
 | 
			
		||||
                            name: 'ignoreFeatureStrategyVariant',
 | 
			
		||||
                            weightType: 'variable',
 | 
			
		||||
                            weight: 1000,
 | 
			
		||||
                            stickiness: 'default',
 | 
			
		||||
                        },
 | 
			
		||||
                    ],
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            context: { appName: 'client-test' },
 | 
			
		||||
            logError: console.log,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        expect(client.getVariant(name, {}).name).toEqual('strategyVariant');
 | 
			
		||||
        expect(client.getVariant(name, {}).enabled).toBeTruthy();
 | 
			
		||||
        expect(client.isEnabled(name, {}).result).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it(`returns '${playgroundStrategyEvaluation.unknownResult}' if it can't evaluate a feature`, async () => {
 | 
			
		||||
        const name = 'toggle-name';
 | 
			
		||||
        const context = { appName: 'client-test' };
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,13 @@ export const mapFeaturesForClient = (
 | 
			
		||||
        strategies: feature.strategies.map((strategy) => ({
 | 
			
		||||
            parameters: {},
 | 
			
		||||
            ...strategy,
 | 
			
		||||
            variants: (strategy.variants || []).map((variant) => ({
 | 
			
		||||
                ...variant,
 | 
			
		||||
                payload: variant.payload && {
 | 
			
		||||
                    ...variant.payload,
 | 
			
		||||
                    type: variant.payload.type as PayloadType,
 | 
			
		||||
                },
 | 
			
		||||
            })),
 | 
			
		||||
            constraints:
 | 
			
		||||
                strategy.constraints &&
 | 
			
		||||
                strategy.constraints.map((constraint) => ({
 | 
			
		||||
 | 
			
		||||
@ -180,7 +180,10 @@ export class PlaygroundService {
 | 
			
		||||
                        name: feature.name,
 | 
			
		||||
                        environment,
 | 
			
		||||
                        context,
 | 
			
		||||
                        variants: variantsMap[feature.name] || [],
 | 
			
		||||
                        variants:
 | 
			
		||||
                            strategyEvaluationResult.variants ||
 | 
			
		||||
                            variantsMap[feature.name] ||
 | 
			
		||||
                            [],
 | 
			
		||||
                    };
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -20,6 +20,19 @@ exports[`featureSchema constraints 1`] = `
 | 
			
		||||
exports[`featureSchema variant override values must be an array 1`] = `
 | 
			
		||||
{
 | 
			
		||||
  "errors": [
 | 
			
		||||
    {
 | 
			
		||||
      "instancePath": "/variants/0/payload/type",
 | 
			
		||||
      "keyword": "enum",
 | 
			
		||||
      "message": "must be equal to one of the allowed values",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "allowedValues": [
 | 
			
		||||
          "json",
 | 
			
		||||
          "csv",
 | 
			
		||||
          "string",
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      "schemaPath": "#/properties/payload/properties/type/enum",
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "instancePath": "/variants/0/overrides/0/values",
 | 
			
		||||
      "keyword": "type",
 | 
			
		||||
 | 
			
		||||
@ -93,7 +93,7 @@ export const advancedPlaygroundEnvironmentFeatureSchema = {
 | 
			
		||||
            description: `The feature variant you receive based on the provided context or the _disabled
 | 
			
		||||
                          variant_. If a feature is disabled or doesn't have any
 | 
			
		||||
                          variants, you would get the _disabled variant_.
 | 
			
		||||
                          Otherwise, you'll get one of thefeature's defined variants.`,
 | 
			
		||||
                          Otherwise, you'll get one of the feature's defined variants.`,
 | 
			
		||||
            type: 'object',
 | 
			
		||||
            additionalProperties: false,
 | 
			
		||||
            required: ['name', 'enabled'],
 | 
			
		||||
@ -118,7 +118,6 @@ export const advancedPlaygroundEnvironmentFeatureSchema = {
 | 
			
		||||
                        type: {
 | 
			
		||||
                            description: 'The format of the payload.',
 | 
			
		||||
                            type: 'string',
 | 
			
		||||
                            enum: ['json', 'csv', 'string'],
 | 
			
		||||
                        },
 | 
			
		||||
                        value: {
 | 
			
		||||
                            type: 'string',
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ test('clientFeaturesSchema required fields', () => {
 | 
			
		||||
                        weightType: 'fix',
 | 
			
		||||
                        stickiness: 'c',
 | 
			
		||||
                        payload: {
 | 
			
		||||
                            type: 'a',
 | 
			
		||||
                            type: 'string',
 | 
			
		||||
                            value: 'b',
 | 
			
		||||
                        },
 | 
			
		||||
                        overrides: [
 | 
			
		||||
 | 
			
		||||
@ -43,6 +43,7 @@ export const createStrategyVariantSchema = {
 | 
			
		||||
                    description:
 | 
			
		||||
                        'The type of the value. Commonly used types are string, json and csv.',
 | 
			
		||||
                    type: 'string',
 | 
			
		||||
                    enum: ['json', 'csv', 'string'],
 | 
			
		||||
                },
 | 
			
		||||
                value: {
 | 
			
		||||
                    description: 'The actual value of payload',
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ test('featureSchema', () => {
 | 
			
		||||
                weightType: 'fix',
 | 
			
		||||
                stickiness: 'a',
 | 
			
		||||
                overrides: [{ contextName: 'a', values: ['a'] }],
 | 
			
		||||
                payload: { type: 'a', value: 'b' },
 | 
			
		||||
                payload: { type: 'string', value: 'b' },
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        environments: [
 | 
			
		||||
 | 
			
		||||
@ -108,7 +108,6 @@ export const playgroundFeatureSchema = {
 | 
			
		||||
                        type: {
 | 
			
		||||
                            description: 'The format of the payload.',
 | 
			
		||||
                            type: 'string',
 | 
			
		||||
                            enum: ['json', 'csv', 'string'],
 | 
			
		||||
                        },
 | 
			
		||||
                        value: {
 | 
			
		||||
                            type: 'string',
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,8 @@ import { FromSchema } from 'json-schema-to-ts';
 | 
			
		||||
import { parametersSchema } from './parameters-schema';
 | 
			
		||||
import { playgroundConstraintSchema } from './playground-constraint-schema';
 | 
			
		||||
import { playgroundSegmentSchema } from './playground-segment-schema';
 | 
			
		||||
import { variantSchema } from './variant-schema';
 | 
			
		||||
import { overrideSchema } from './override-schema';
 | 
			
		||||
 | 
			
		||||
export const playgroundStrategyEvaluation = {
 | 
			
		||||
    evaluationComplete: 'complete',
 | 
			
		||||
@ -51,6 +53,55 @@ export const strategyEvaluationResults = {
 | 
			
		||||
                    description:
 | 
			
		||||
                        'Whether this strategy evaluates to true or not.',
 | 
			
		||||
                },
 | 
			
		||||
                variant: {
 | 
			
		||||
                    description: `The feature variant you receive based on the provided context or the _disabled
 | 
			
		||||
                          variant_. If a feature is disabled or doesn't have any
 | 
			
		||||
                          variants, you would get the _disabled variant_.
 | 
			
		||||
                          Otherwise, you'll get one of the feature's defined variants.`,
 | 
			
		||||
                    type: 'object',
 | 
			
		||||
                    additionalProperties: false,
 | 
			
		||||
                    required: ['name', 'enabled'],
 | 
			
		||||
                    properties: {
 | 
			
		||||
                        name: {
 | 
			
		||||
                            type: 'string',
 | 
			
		||||
                            description:
 | 
			
		||||
                                "The variant's name. If there is no variant or if the toggle is disabled, this will be `disabled`",
 | 
			
		||||
                            example: 'red-variant',
 | 
			
		||||
                        },
 | 
			
		||||
                        enabled: {
 | 
			
		||||
                            type: 'boolean',
 | 
			
		||||
                            description:
 | 
			
		||||
                                "Whether the variant is enabled or not. If the feature is disabled or if it doesn't have variants, this property will be `false`",
 | 
			
		||||
                        },
 | 
			
		||||
                        payload: {
 | 
			
		||||
                            type: 'object',
 | 
			
		||||
                            additionalProperties: false,
 | 
			
		||||
                            required: ['type', 'value'],
 | 
			
		||||
                            description:
 | 
			
		||||
                                'An optional payload attached to the variant.',
 | 
			
		||||
                            properties: {
 | 
			
		||||
                                type: {
 | 
			
		||||
                                    description: 'The format of the payload.',
 | 
			
		||||
                                    type: 'string',
 | 
			
		||||
                                    enum: ['json', 'csv', 'string'],
 | 
			
		||||
                                },
 | 
			
		||||
                                value: {
 | 
			
		||||
                                    type: 'string',
 | 
			
		||||
                                    description:
 | 
			
		||||
                                        'The payload value stringified.',
 | 
			
		||||
                                    example: '{"property": "value"}',
 | 
			
		||||
                                },
 | 
			
		||||
                            },
 | 
			
		||||
                        },
 | 
			
		||||
                    },
 | 
			
		||||
                    nullable: true,
 | 
			
		||||
                    example: { name: 'green', enabled: true },
 | 
			
		||||
                },
 | 
			
		||||
                variants: {
 | 
			
		||||
                    type: 'array',
 | 
			
		||||
                    description: 'The feature variants.',
 | 
			
		||||
                    items: { $ref: variantSchema.$id },
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
@ -139,6 +190,8 @@ export const playgroundStrategySchema = {
 | 
			
		||||
            playgroundConstraintSchema,
 | 
			
		||||
            playgroundSegmentSchema,
 | 
			
		||||
            parametersSchema,
 | 
			
		||||
            variantSchema,
 | 
			
		||||
            overrideSchema,
 | 
			
		||||
        },
 | 
			
		||||
    },
 | 
			
		||||
} as const;
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
import { FromSchema } from 'json-schema-to-ts';
 | 
			
		||||
import { PayloadType } from 'unleash-client';
 | 
			
		||||
 | 
			
		||||
export const proxyFeatureSchema = {
 | 
			
		||||
    $id: '#/components/schemas/proxyFeatureSchema',
 | 
			
		||||
@ -51,7 +50,7 @@ export const proxyFeatureSchema = {
 | 
			
		||||
                        type: {
 | 
			
		||||
                            type: 'string',
 | 
			
		||||
                            description: 'The format of the payload.',
 | 
			
		||||
                            enum: Object.values(PayloadType),
 | 
			
		||||
                            enum: ['json', 'csv', 'string'],
 | 
			
		||||
                        },
 | 
			
		||||
                        value: {
 | 
			
		||||
                            type: 'string',
 | 
			
		||||
 | 
			
		||||
@ -38,11 +38,13 @@ export const variantSchema = {
 | 
			
		||||
            type: 'object',
 | 
			
		||||
            required: ['type', 'value'],
 | 
			
		||||
            description: 'Extra data configured for this variant',
 | 
			
		||||
            additionalProperties: false,
 | 
			
		||||
            properties: {
 | 
			
		||||
                type: {
 | 
			
		||||
                    description:
 | 
			
		||||
                        'The type of the value. Commonly used types are string, json and csv.',
 | 
			
		||||
                    type: 'string',
 | 
			
		||||
                    enum: ['json', 'csv', 'string'],
 | 
			
		||||
                },
 | 
			
		||||
                value: {
 | 
			
		||||
                    description: 'The actual value of payload',
 | 
			
		||||
 | 
			
		||||
@ -122,7 +122,7 @@ export interface IVariant {
 | 
			
		||||
    weight: number;
 | 
			
		||||
    weightType: 'variable' | 'fix';
 | 
			
		||||
    payload?: {
 | 
			
		||||
        type: string;
 | 
			
		||||
        type: 'json' | 'csv' | 'string';
 | 
			
		||||
        value: string;
 | 
			
		||||
    };
 | 
			
		||||
    stickiness: string;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								yarn.lock
									
									
									
									
									
								
							@ -7509,15 +7509,15 @@ universalify@^0.1.0:
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
 | 
			
		||||
  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
 | 
			
		||||
 | 
			
		||||
unleash-client@3.21.0:
 | 
			
		||||
  version "3.21.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/unleash-client/-/unleash-client-3.21.0.tgz#a31ab30acb42abfb3a21180aa83e4415a3124ec1"
 | 
			
		||||
  integrity sha512-I7eYhRyOia3oBZ9Tu1v+IlNO+XJgsjcMEO2+j+e4A7LTTKZvGoV8WPfDGGxiMPKBPHNUACkERB3YhCQ9jzTGoQ==
 | 
			
		||||
unleash-client@4.1.0-beta.5:
 | 
			
		||||
  version "4.1.0-beta.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/unleash-client/-/unleash-client-4.1.0-beta.5.tgz#7407a9dae30411cb2cb849569a6e058cf6b6c47c"
 | 
			
		||||
  integrity sha512-aN5PdvfAlVBc7Fm5cgQr7pc2j6rvbRtp6G9kow0O3FP4h3UCFbM2i0NvSB4r3F8AysXWonbv9IB/TyAC2CGsPA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    ip "^1.1.8"
 | 
			
		||||
    make-fetch-happen "^10.2.1"
 | 
			
		||||
    murmurhash3js "^3.0.1"
 | 
			
		||||
    semver "^7.3.8"
 | 
			
		||||
    semver "^7.5.3"
 | 
			
		||||
 | 
			
		||||
unpipe@1.0.0, unpipe@~1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user