mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-10 17:53:36 +02:00
fix: upgrade unleash-client to v5.3.0 (#5800)
This commit is contained in:
parent
3957abf0f6
commit
68eb3dec07
@ -23,6 +23,7 @@ test('should render environment table', async () => {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
value: 'variantValue',
|
value: 'variantValue',
|
||||||
},
|
},
|
||||||
|
feature_enabled: true,
|
||||||
},
|
},
|
||||||
environment: 'dev',
|
environment: 'dev',
|
||||||
context: {
|
context: {
|
||||||
|
@ -14,6 +14,8 @@ import type { AdvancedPlaygroundEnvironmentFeatureSchemaVariantPayload } from '.
|
|||||||
export type AdvancedPlaygroundEnvironmentFeatureSchemaVariant = {
|
export type AdvancedPlaygroundEnvironmentFeatureSchemaVariant = {
|
||||||
/** Whether the variant is enabled or not. If the feature is disabled or if it doesn't have variants, this property will be `false` */
|
/** Whether the variant is enabled or not. If the feature is disabled or if it doesn't have variants, this property will be `false` */
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
/** Whether the feature is enabled or not. If the feature is disabled, this property will be `false` */
|
||||||
|
feature_enabled?: boolean;
|
||||||
/** The variant's name. If there is no variant or if the toggle is disabled, this will be `disabled` */
|
/** The variant's name. If there is no variant or if the toggle is disabled, this will be `disabled` */
|
||||||
name: string;
|
name: string;
|
||||||
/** An optional payload attached to the variant. */
|
/** An optional payload attached to the variant. */
|
||||||
|
@ -11,6 +11,8 @@ import type { ProxyFeatureSchemaVariantPayload } from './proxyFeatureSchemaVaria
|
|||||||
export type ProxyFeatureSchemaVariant = {
|
export type ProxyFeatureSchemaVariant = {
|
||||||
/** Whether the variant is enabled or not. */
|
/** Whether the variant is enabled or not. */
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
/** Whether the feature is enabled or not. */
|
||||||
|
feature_enabled?: boolean;
|
||||||
/** The variants name. Is unique for this feature toggle */
|
/** The variants name. Is unique for this feature toggle */
|
||||||
name: string;
|
name: string;
|
||||||
/** Extra data configured for this variant */
|
/** Extra data configured for this variant */
|
||||||
|
@ -15,4 +15,5 @@ export const ProxyFeatureSchemaVariantPayloadType = {
|
|||||||
json: 'json',
|
json: 'json',
|
||||||
csv: 'csv',
|
csv: 'csv',
|
||||||
string: 'string',
|
string: 'string',
|
||||||
|
number: 'number',
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -11,6 +11,8 @@ import type { VariantFlagSchemaPayload } from './variantFlagSchemaPayload';
|
|||||||
export interface VariantFlagSchema {
|
export interface VariantFlagSchema {
|
||||||
/** Whether the variant is enabled or not. */
|
/** Whether the variant is enabled or not. */
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
|
/** Whether the feature is enabled or not. */
|
||||||
|
feature_enabled?: boolean;
|
||||||
/** The name of the variant. Will always be disabled if `enabled` is false. */
|
/** The name of the variant. Will always be disabled if `enabled` is false. */
|
||||||
name?: string;
|
name?: string;
|
||||||
/** Additional data associated with this variant. */
|
/** Additional data associated with this variant. */
|
||||||
|
@ -15,4 +15,5 @@ export const VariantFlagSchemaPayloadType = {
|
|||||||
string: 'string',
|
string: 'string',
|
||||||
json: 'json',
|
json: 'json',
|
||||||
csv: 'csv',
|
csv: 'csv',
|
||||||
|
number: 'number',
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -145,7 +145,7 @@
|
|||||||
"stoppable": "^1.1.0",
|
"stoppable": "^1.1.0",
|
||||||
"ts-toolbelt": "^9.6.0",
|
"ts-toolbelt": "^9.6.0",
|
||||||
"type-is": "^1.6.18",
|
"type-is": "^1.6.18",
|
||||||
"unleash-client": "5.0.0",
|
"unleash-client": "5.3.1",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -120,6 +120,7 @@ test('advanced playground evaluation with parent dependency', async () => {
|
|||||||
expect(child.variant).toEqual({
|
expect(child.variant).toEqual({
|
||||||
name: 'disabled',
|
name: 'disabled',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
feature_enabled: false,
|
||||||
});
|
});
|
||||||
expect(parent.hasUnsatisfiedDependency).toBe(false);
|
expect(parent.hasUnsatisfiedDependency).toBe(false);
|
||||||
expect(parent.isEnabled).toBe(false);
|
expect(parent.isEnabled).toBe(false);
|
||||||
@ -298,8 +299,7 @@ test('advanced playground evaluation happy path', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('show matching variant from variants selection only for enabled toggles', async () => {
|
test('show matching variant from variants selection only for enabled toggles', async () => {
|
||||||
const variants = [
|
const variant = {
|
||||||
{
|
|
||||||
stickiness: 'random',
|
stickiness: 'random',
|
||||||
name: 'a',
|
name: 'a',
|
||||||
weight: 1000,
|
weight: 1000,
|
||||||
@ -308,8 +308,8 @@ test('show matching variant from variants selection only for enabled toggles', a
|
|||||||
value: 'aval',
|
value: 'aval',
|
||||||
},
|
},
|
||||||
weightType: 'variable',
|
weightType: 'variable',
|
||||||
},
|
};
|
||||||
];
|
|
||||||
await createFeatureToggleWithStrategy(
|
await createFeatureToggleWithStrategy(
|
||||||
'test-playground-feature-with-variants',
|
'test-playground-feature-with-variants',
|
||||||
{
|
{
|
||||||
@ -320,7 +320,7 @@ test('show matching variant from variants selection only for enabled toggles', a
|
|||||||
stickiness: 'random',
|
stickiness: 'random',
|
||||||
groupId: 'test-playground-feature-with-variants',
|
groupId: 'test-playground-feature-with-variants',
|
||||||
},
|
},
|
||||||
variants,
|
variants: [variant],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -347,7 +347,7 @@ test('show matching variant from variants selection only for enabled toggles', a
|
|||||||
|
|
||||||
enabledFeatures.forEach((feature) => {
|
enabledFeatures.forEach((feature) => {
|
||||||
expect(feature.variant?.name).toBe('a');
|
expect(feature.variant?.name).toBe('a');
|
||||||
expect(feature.variants).toMatchObject(variants);
|
expect(feature.variants).toMatchObject([variant]);
|
||||||
});
|
});
|
||||||
disabledFeatures.forEach((feature) => {
|
disabledFeatures.forEach((feature) => {
|
||||||
expect(feature.variant?.name).toBe('disabled');
|
expect(feature.variant?.name).toBe('disabled');
|
||||||
|
@ -11,17 +11,18 @@ import { Context } from './context';
|
|||||||
import { SegmentForEvaluation } from './strategy/strategy';
|
import { SegmentForEvaluation } from './strategy/strategy';
|
||||||
import { PlaygroundStrategySchema } from '../../../openapi/spec/playground-strategy-schema';
|
import { PlaygroundStrategySchema } from '../../../openapi/spec/playground-strategy-schema';
|
||||||
import { playgroundStrategyEvaluation } from '../../../openapi/spec/playground-strategy-schema';
|
import { playgroundStrategyEvaluation } from '../../../openapi/spec/playground-strategy-schema';
|
||||||
|
import { randomId } from '../../../util';
|
||||||
export type StrategyEvaluationResult = Pick<
|
|
||||||
PlaygroundStrategySchema,
|
|
||||||
'result' | 'segments' | 'constraints'
|
|
||||||
>;
|
|
||||||
|
|
||||||
export type EvaluatedPlaygroundStrategy = Omit<
|
export type EvaluatedPlaygroundStrategy = Omit<
|
||||||
PlaygroundStrategySchema,
|
PlaygroundStrategySchema,
|
||||||
'links'
|
'links'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type StrategyEvaluationResult = Pick<
|
||||||
|
EvaluatedPlaygroundStrategy,
|
||||||
|
'result' | 'segments' | 'constraints'
|
||||||
|
>;
|
||||||
|
|
||||||
export type FeatureStrategiesEvaluationResult = {
|
export type FeatureStrategiesEvaluationResult = {
|
||||||
result: boolean | typeof playgroundStrategyEvaluation.unknownResult;
|
result: boolean | typeof playgroundStrategyEvaluation.unknownResult;
|
||||||
variant?: Variant;
|
variant?: Variant;
|
||||||
@ -144,26 +145,32 @@ export default class UnleashClient {
|
|||||||
|
|
||||||
const strategies = feature.strategies.map(
|
const strategies = feature.strategies.map(
|
||||||
(strategySelector): EvaluatedPlaygroundStrategy => {
|
(strategySelector): EvaluatedPlaygroundStrategy => {
|
||||||
const getStrategy = () => {
|
const getStrategy = (): Strategy => {
|
||||||
|
// assume that 'unknown' strategy is always present
|
||||||
|
const unknownStrategy = this.getStrategy(
|
||||||
|
'unknown',
|
||||||
|
) as Strategy;
|
||||||
|
|
||||||
// the application hostname strategy relies on external
|
// the application hostname strategy relies on external
|
||||||
// variables to calculate its result. As such, we can't
|
// variables to calculate its result. As such, we can't
|
||||||
// evaluate it in a way that makes sense. So we'll
|
// evaluate it in a way that makes sense. So we'll
|
||||||
// use the 'unknown' strategy instead.
|
// use the 'unknown' strategy instead.
|
||||||
if (strategySelector.name === 'applicationHostname') {
|
if (strategySelector.name === 'applicationHostname') {
|
||||||
return this.getStrategy('unknown');
|
return unknownStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
this.getStrategy(strategySelector.name) ??
|
this.getStrategy(strategySelector.name) ??
|
||||||
this.getStrategy('unknown')
|
unknownStrategy
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const strategy = getStrategy();
|
const strategy = getStrategy();
|
||||||
|
|
||||||
const segments =
|
const segments =
|
||||||
strategySelector.segments
|
(strategySelector.segments
|
||||||
?.map(this.getSegment(this.repository))
|
?.map(this.getSegment(this.repository))
|
||||||
.filter(Boolean) ?? [];
|
.filter(Boolean) as SegmentForEvaluation[]) ?? [];
|
||||||
|
|
||||||
const evaluationResult = strategy.isEnabledWithConstraints(
|
const evaluationResult = strategy.isEnabledWithConstraints(
|
||||||
strategySelector.parameters,
|
strategySelector.parameters,
|
||||||
@ -176,7 +183,7 @@ export default class UnleashClient {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: strategySelector.name,
|
name: strategySelector.name,
|
||||||
id: strategySelector.id,
|
id: strategySelector.id || randomId(),
|
||||||
title: strategySelector.title,
|
title: strategySelector.title,
|
||||||
disabled: strategySelector.disabled || false,
|
disabled: strategySelector.disabled || false,
|
||||||
parameters: strategySelector.parameters,
|
parameters: strategySelector.parameters,
|
||||||
@ -189,7 +196,7 @@ export default class UnleashClient {
|
|||||||
const overallStrategyResult = (): [
|
const overallStrategyResult = (): [
|
||||||
boolean | typeof playgroundStrategyEvaluation.unknownResult,
|
boolean | typeof playgroundStrategyEvaluation.unknownResult,
|
||||||
VariantDefinition[] | undefined,
|
VariantDefinition[] | undefined,
|
||||||
Variant | undefined | null,
|
Variant | undefined,
|
||||||
] => {
|
] => {
|
||||||
// if at least one strategy is enabled, then the feature is enabled
|
// if at least one strategy is enabled, then the feature is enabled
|
||||||
const enabledStrategy = strategies.find(
|
const enabledStrategy = strategies.find(
|
||||||
@ -202,7 +209,7 @@ export default class UnleashClient {
|
|||||||
return [
|
return [
|
||||||
true,
|
true,
|
||||||
enabledStrategy.result.variants,
|
enabledStrategy.result.variants,
|
||||||
enabledStrategy.result.variant,
|
enabledStrategy.result.variant || undefined,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +291,11 @@ export default class UnleashClient {
|
|||||||
'result' | 'variant'
|
'result' | 'variant'
|
||||||
>,
|
>,
|
||||||
): Variant {
|
): Variant {
|
||||||
const fallback = fallbackVariant || getDefaultVariant();
|
const fallback = {
|
||||||
|
feature_enabled: false,
|
||||||
|
featureEnabled: false,
|
||||||
|
...(fallbackVariant || getDefaultVariant()),
|
||||||
|
};
|
||||||
const feature = this.repository.getToggle(name);
|
const feature = this.repository.getToggle(name);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -294,13 +305,14 @@ export default class UnleashClient {
|
|||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
let enabled = true;
|
|
||||||
const result =
|
const result =
|
||||||
forcedResult ??
|
forcedResult ??
|
||||||
this.isFeatureEnabled(feature, context, () =>
|
this.isFeatureEnabled(feature, context, () =>
|
||||||
fallbackVariant ? fallbackVariant.enabled : false,
|
fallbackVariant ? fallbackVariant.enabled : false,
|
||||||
);
|
);
|
||||||
enabled = result.result === true;
|
const enabled = result.result === true;
|
||||||
|
fallback.feature_enabled = fallbackVariant?.feature_enabled ?? enabled;
|
||||||
|
fallback.featureEnabled = fallback.feature_enabled;
|
||||||
const strategyVariant = result.variant;
|
const strategyVariant = result.variant;
|
||||||
if (enabled && strategyVariant) {
|
if (enabled && strategyVariant) {
|
||||||
return strategyVariant;
|
return strategyVariant;
|
||||||
@ -330,6 +342,8 @@ export default class UnleashClient {
|
|||||||
name: variant.name,
|
name: variant.name,
|
||||||
payload: variant.payload,
|
payload: variant.payload,
|
||||||
enabled,
|
enabled,
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
resolveBootstrapProvider,
|
resolveBootstrapProvider,
|
||||||
} from './repository/bootstrap-provider';
|
} from './repository/bootstrap-provider';
|
||||||
import { StorageProvider } from './repository/storage-provider';
|
import { StorageProvider } from './repository/storage-provider';
|
||||||
|
import InMemStorageProvider from './repository/storage-provider-in-mem';
|
||||||
|
|
||||||
export { Strategy };
|
export { Strategy };
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ export class FeatureEvaluator {
|
|||||||
strategies = [],
|
strategies = [],
|
||||||
repository,
|
repository,
|
||||||
bootstrap = { data: [] },
|
bootstrap = { data: [] },
|
||||||
storageProvider,
|
storageProvider = new InMemStorageProvider(),
|
||||||
}: FeatureEvaluatorConfig) {
|
}: FeatureEvaluatorConfig) {
|
||||||
this.staticContext = { appName, environment };
|
this.staticContext = { appName, environment };
|
||||||
|
|
||||||
@ -52,7 +53,7 @@ export class FeatureEvaluator {
|
|||||||
new Repository({
|
new Repository({
|
||||||
appName,
|
appName,
|
||||||
bootstrapProvider,
|
bootstrapProvider,
|
||||||
storageProvider: storageProvider,
|
storageProvider,
|
||||||
});
|
});
|
||||||
|
|
||||||
// setup client
|
// setup client
|
||||||
|
@ -26,6 +26,11 @@ export interface Variant {
|
|||||||
name: string;
|
name: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
payload?: Payload;
|
payload?: Payload;
|
||||||
|
featureEnabled?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated use featureEnabled
|
||||||
|
*/
|
||||||
|
feature_enabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultVariant(): Variant {
|
export function getDefaultVariant(): Variant {
|
||||||
|
@ -27,7 +27,7 @@ export const offlineUnleashClientNode = async ({
|
|||||||
storageProvider: new InMemStorageProviderNode(),
|
storageProvider: new InMemStorageProviderNode(),
|
||||||
bootstrap: {
|
bootstrap: {
|
||||||
data: mapFeaturesForClient(features),
|
data: mapFeaturesForClient(features),
|
||||||
segments: mapSegmentsForClient(segments),
|
segments: mapSegmentsForClient(segments || []),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,8 +5,9 @@ import { Segment } from './feature-evaluator/strategy/strategy';
|
|||||||
import { ISegment } from '../../types/model';
|
import { ISegment } from '../../types/model';
|
||||||
import { serializeDates } from '../../types/serialize-dates';
|
import { serializeDates } from '../../types/serialize-dates';
|
||||||
import { Operator } from './feature-evaluator/constraint';
|
import { Operator } from './feature-evaluator/constraint';
|
||||||
import { FeatureInterface } from 'unleash-client/lib/feature';
|
|
||||||
import { PayloadType } from 'unleash-client';
|
import { PayloadType } from 'unleash-client';
|
||||||
|
import { FeatureInterface } from 'unleash-client/lib/feature';
|
||||||
|
import { FeatureInterface as PlaygroundFeatureInterface } from './feature-evaluator/feature';
|
||||||
|
|
||||||
type NonEmptyList<T> = [T, ...T[]];
|
type NonEmptyList<T> = [T, ...T[]];
|
||||||
|
|
||||||
@ -28,6 +29,8 @@ export const mapFeaturesForClient = (
|
|||||||
strategies: feature.strategies.map((strategy) => ({
|
strategies: feature.strategies.map((strategy) => ({
|
||||||
parameters: {},
|
parameters: {},
|
||||||
...strategy,
|
...strategy,
|
||||||
|
title: strategy.title ?? undefined,
|
||||||
|
disabled: strategy.disabled ?? false,
|
||||||
variants: (strategy.variants || []).map((variant) => ({
|
variants: (strategy.variants || []).map((variant) => ({
|
||||||
...variant,
|
...variant,
|
||||||
payload: variant.payload && {
|
payload: variant.payload && {
|
||||||
@ -35,12 +38,13 @@ export const mapFeaturesForClient = (
|
|||||||
type: variant.payload.type as PayloadType,
|
type: variant.payload.type as PayloadType,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
constraints: strategy.constraints?.map((constraint) => ({
|
constraints:
|
||||||
|
strategy.constraints?.map((constraint) => ({
|
||||||
inverted: false,
|
inverted: false,
|
||||||
values: [],
|
values: [],
|
||||||
...constraint,
|
...constraint,
|
||||||
operator: constraint.operator as unknown as Operator,
|
operator: constraint.operator as unknown as Operator,
|
||||||
})),
|
})) || [],
|
||||||
})),
|
})),
|
||||||
dependencies: feature.dependencies,
|
dependencies: feature.dependencies,
|
||||||
}));
|
}));
|
||||||
@ -65,8 +69,11 @@ export const offlineUnleashClient = async ({
|
|||||||
appName: context.appName,
|
appName: context.appName,
|
||||||
storageProvider: new InMemStorageProvider(),
|
storageProvider: new InMemStorageProvider(),
|
||||||
bootstrap: {
|
bootstrap: {
|
||||||
data: mapFeaturesForClient(features),
|
// FIXME: mismatch between playground and proxy types
|
||||||
segments: mapSegmentsForClient(segments),
|
data: mapFeaturesForClient(
|
||||||
|
features,
|
||||||
|
) as PlaygroundFeatureInterface[],
|
||||||
|
segments: mapSegmentsForClient(segments || []),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -203,6 +203,17 @@ export class PlaygroundService {
|
|||||||
feature.enabled &&
|
feature.enabled &&
|
||||||
!hasUnsatisfiedDependency;
|
!hasUnsatisfiedDependency;
|
||||||
|
|
||||||
|
const variant = {
|
||||||
|
...(isEnabled
|
||||||
|
? client.forceGetVariant(
|
||||||
|
feature.name,
|
||||||
|
strategyEvaluationResult,
|
||||||
|
clientContext,
|
||||||
|
)
|
||||||
|
: getDefaultVariant()),
|
||||||
|
feature_enabled: isEnabled,
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isEnabled,
|
isEnabled,
|
||||||
isEnabledInCurrentEnvironment: feature.enabled,
|
isEnabledInCurrentEnvironment: feature.enabled,
|
||||||
@ -212,13 +223,7 @@ export class PlaygroundService {
|
|||||||
data: strategyEvaluationResult.strategies,
|
data: strategyEvaluationResult.strategies,
|
||||||
},
|
},
|
||||||
projectId: featureProject[feature.name],
|
projectId: featureProject[feature.name],
|
||||||
variant: isEnabled
|
variant,
|
||||||
? client.forceGetVariant(
|
|
||||||
feature.name,
|
|
||||||
strategyEvaluationResult,
|
|
||||||
clientContext,
|
|
||||||
)
|
|
||||||
: getDefaultVariant(),
|
|
||||||
name: feature.name,
|
name: feature.name,
|
||||||
environment,
|
environment,
|
||||||
context,
|
context,
|
||||||
|
@ -127,6 +127,11 @@ export const advancedPlaygroundEnvironmentFeatureSchema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
feature_enabled: {
|
||||||
|
type: 'boolean',
|
||||||
|
description:
|
||||||
|
'Whether the feature is enabled or not. If the feature is disabled, this property will be `false`',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
nullable: true,
|
nullable: true,
|
||||||
example: { name: 'green', enabled: true },
|
example: { name: 'green', enabled: true },
|
||||||
|
@ -121,6 +121,17 @@ export const playgroundFeatureSchema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
feature_enabled: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Use `featureEnabled` instead.',
|
||||||
|
example: true,
|
||||||
|
},
|
||||||
|
featureEnabled: {
|
||||||
|
deprecated: true,
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether the feature is enabled or not.',
|
||||||
|
example: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
nullable: true,
|
nullable: true,
|
||||||
example: { name: 'green', enabled: true },
|
example: { name: 'green', enabled: true },
|
||||||
|
@ -50,7 +50,7 @@ export const proxyFeatureSchema = {
|
|||||||
type: {
|
type: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The format of the payload.',
|
description: 'The format of the payload.',
|
||||||
enum: ['json', 'csv', 'string'],
|
enum: ['json', 'csv', 'string', 'number'],
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -58,6 +58,17 @@ export const proxyFeatureSchema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
feature_enabled: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether the feature is enabled or not.',
|
||||||
|
example: true,
|
||||||
|
},
|
||||||
|
featureEnabled: {
|
||||||
|
deprecated: true,
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Use `feature_enabled` instead.',
|
||||||
|
example: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -25,7 +25,7 @@ export const variantFlagSchema = {
|
|||||||
type: {
|
type: {
|
||||||
description: 'The type of data contained.',
|
description: 'The type of data contained.',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
enum: ['string', 'json', 'csv'],
|
enum: ['string', 'json', 'csv', 'number'],
|
||||||
example: 'json',
|
example: 'json',
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
@ -35,6 +35,17 @@ export const variantFlagSchema = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
feature_enabled: {
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Whether the feature is enabled or not.',
|
||||||
|
example: true,
|
||||||
|
},
|
||||||
|
featureEnabled: {
|
||||||
|
deprecated: true,
|
||||||
|
type: 'boolean',
|
||||||
|
description: 'Use `feature_enabled` instead.',
|
||||||
|
example: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {},
|
components: {},
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -449,13 +449,23 @@ test('should filter features by enabled/disabled', async () => {
|
|||||||
name: 'enabledFeature1',
|
name: 'enabledFeature1',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enabledFeature2',
|
name: 'enabledFeature2',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -486,7 +496,12 @@ test('should filter features by strategies', async () => {
|
|||||||
name: 'featureWithMultipleStrategies',
|
name: 'featureWithMultipleStrategies',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -629,7 +644,12 @@ test('should filter features by project', async () => {
|
|||||||
name: 'featureInProjectDefault',
|
name: 'featureInProjectDefault',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -646,7 +666,12 @@ test('should filter features by project', async () => {
|
|||||||
name: 'featureInProjectA',
|
name: 'featureInProjectA',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -663,13 +688,23 @@ test('should filter features by project', async () => {
|
|||||||
name: 'featureInProjectA',
|
name: 'featureInProjectA',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'featureInProjectB',
|
name: 'featureInProjectB',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -743,7 +778,12 @@ test('should filter features by environment', async () => {
|
|||||||
name: 'featureInEnvironmentDefault',
|
name: 'featureInEnvironmentDefault',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -760,7 +800,12 @@ test('should filter features by environment', async () => {
|
|||||||
name: 'featureInEnvironmentA',
|
name: 'featureInEnvironmentA',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -777,7 +822,12 @@ test('should filter features by environment', async () => {
|
|||||||
name: 'featureInEnvironmentB',
|
name: 'featureInEnvironmentB',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -1040,7 +1090,12 @@ test('should evaluate strategies when returning toggles', async () => {
|
|||||||
name: 'enabledFeature',
|
name: 'enabledFeature',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -1095,13 +1150,23 @@ test('should not return all features', async () => {
|
|||||||
name: 'enabledFeature1',
|
name: 'enabledFeature1',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'enabledFeature2',
|
name: 'enabledFeature2',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -1197,7 +1262,12 @@ test('should NOT evaluate disabled strategies when returning toggles', async ()
|
|||||||
name: 'enabledFeature',
|
name: 'enabledFeature',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
impressionData: false,
|
impressionData: false,
|
||||||
variant: { enabled: false, name: 'disabled' },
|
variant: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled',
|
||||||
|
feature_enabled: true,
|
||||||
|
featureEnabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -177,13 +177,12 @@ export const seedDatabaseForPlaygroundTest = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('the playground service (e2e)', () => {
|
describe('the playground service (e2e)', () => {
|
||||||
const isDisabledVariant = ({
|
const isDisabledVariant = (
|
||||||
name,
|
variant?: {
|
||||||
enabled,
|
|
||||||
}: {
|
|
||||||
name: string;
|
name: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}) => name === 'disabled' && !enabled;
|
} | null,
|
||||||
|
) => variant?.name === 'disabled' && !variant?.enabled;
|
||||||
|
|
||||||
const insertAndEvaluateFeatures = async ({
|
const insertAndEvaluateFeatures = async ({
|
||||||
features,
|
features,
|
||||||
@ -290,12 +289,12 @@ describe('the playground service (e2e)', () => {
|
|||||||
ctx.log(JSON.stringify(enabledStateMatches));
|
ctx.log(JSON.stringify(enabledStateMatches));
|
||||||
ctx.log(
|
ctx.log(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
feature.variant.name === 'disabled',
|
feature.variant?.name === 'disabled',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
ctx.log(
|
ctx.log(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
feature.variant.enabled === false,
|
feature.variant?.enabled === false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
@ -704,7 +703,7 @@ describe('the playground service (e2e)', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
feature.strategies.forEach(
|
feature.strategies?.forEach(
|
||||||
({ segments, ...strategy }) => {
|
({ segments, ...strategy }) => {
|
||||||
expect(cleanedReceivedStrategies).toEqual(
|
expect(cleanedReceivedStrategies).toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
@ -845,7 +844,7 @@ describe('the playground service (e2e)', () => {
|
|||||||
features: features.map((feature) => ({
|
features: features.map((feature) => ({
|
||||||
...feature,
|
...feature,
|
||||||
// remove any constraints and use a name that doesn't exist
|
// remove any constraints and use a name that doesn't exist
|
||||||
strategies: feature.strategies.map(
|
strategies: feature.strategies?.map(
|
||||||
(strategy) => ({
|
(strategy) => ({
|
||||||
...strategy,
|
...strategy,
|
||||||
name: 'bogus-strategy',
|
name: 'bogus-strategy',
|
||||||
@ -907,7 +906,7 @@ describe('the playground service (e2e)', () => {
|
|||||||
features: features.map((feature) => ({
|
features: features.map((feature) => ({
|
||||||
...feature,
|
...feature,
|
||||||
// use a constraint that will never be true
|
// use a constraint that will never be true
|
||||||
strategies: feature.strategies.map(
|
strategies: feature.strategies?.map(
|
||||||
(strategy) => ({
|
(strategy) => ({
|
||||||
...strategy,
|
...strategy,
|
||||||
name: 'bogusStrategy',
|
name: 'bogusStrategy',
|
||||||
@ -1223,6 +1222,7 @@ describe('the playground service (e2e)', () => {
|
|||||||
expect(feature.variant).toEqual({
|
expect(feature.variant).toEqual({
|
||||||
name: 'disabled',
|
name: 'disabled',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
feature_enabled: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -7134,10 +7134,10 @@ universalify@^0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
|
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
|
||||||
integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
|
integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
|
||||||
|
|
||||||
unleash-client@5.0.0:
|
unleash-client@5.3.1:
|
||||||
version "5.0.0"
|
version "5.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/unleash-client/-/unleash-client-5.0.0.tgz#b6725723b38f5572b4b1c5261c1bb2254148ec58"
|
resolved "https://registry.yarnpkg.com/unleash-client/-/unleash-client-5.3.1.tgz#8647ea5f8905f21e5fdcb87a79ce91c27a92b605"
|
||||||
integrity sha512-9x3SOpHTnMDY0CosKwy/0Hi9gIjw65+i2fsC76bvYaUIVTlqDoPdCfosBokNIZ/IjCF0TjEgSZefSWNGY5SC9A==
|
integrity sha512-S5WtDNJa9j/JC+tXB/LoBNDMwhtuQ5CxFWge8igoVSspyyypcrKtyd98TSODBeyfzKW0daCyLmE7Pr+jDfROLQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
ip "^1.1.8"
|
ip "^1.1.8"
|
||||||
make-fetch-happen "^10.2.1"
|
make-fetch-happen "^10.2.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user