Skip to content

Commit

Permalink
feat: variant feature status - client specification update
Browse files Browse the repository at this point in the history
  • Loading branch information
Tymek committed Nov 9, 2023
1 parent 077ea75 commit 51e601b
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 109 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"@types/sinon": "^10.0.15",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@unleash/client-specification": "^5.0.2",
"@unleash/client-specification": "^5.1.0",
"ava": "^5.3.0",
"coveralls": "^3.1.1",
"cross-env": "^7.0.3",
Expand Down
61 changes: 30 additions & 31 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { EventEmitter } from 'events';
import { Strategy, StrategyTransportInterface } from './strategy';
import { FeatureInterface } from './feature';
import { RepositoryInterface } from './repository';
import {
Variant, VariantDefinition,
getDefaultVariant, selectVariant, VariantWithFeatureStatus,
} from './variant';
import { Variant, VariantDefinition, defaultVariant, selectVariant } from './variant';
import { Context } from './context';
import { Constraint, Segment, StrategyResult } from './strategy/strategy';
import { createImpressionEvent, UnleashEvents } from './events';
Expand Down Expand Up @@ -46,9 +43,11 @@ export default class UnleashClient extends EventEmitter {
return this.strategies.find((strategy: Strategy): boolean => strategy.name === name);
}

warnStrategyOnce(missingStrategy: string,
name: string,
strategies: StrategyTransportInterface[]) {
warnStrategyOnce(
missingStrategy: string,
name: string,
strategies: StrategyTransportInterface[],
) {
if (!this.warnedStrategies[missingStrategy + name]) {
this.warnedStrategies[missingStrategy + name] = true;
this.emit(
Expand All @@ -75,7 +74,7 @@ export default class UnleashClient extends EventEmitter {
return true;
}

return feature.dependencies.every(parent => {
return feature.dependencies.every((parent) => {
const parentToggle = this.repository.getToggle(parent.feature);

if (!parentToggle) {
Expand All @@ -88,7 +87,10 @@ export default class UnleashClient extends EventEmitter {

if (parent.enabled !== false) {
if (parent.variants?.length) {
const {name, featureEnabled} = this.getVariant(parent.feature, context);
const { name, feature_enabled: featureEnabled } = this.getVariant(
parent.feature,
context,
);
return featureEnabled && parent.variants.includes(name);
}
return this.isEnabled(parent.feature, context, () => false);
Expand Down Expand Up @@ -149,11 +151,12 @@ export default class UnleashClient extends EventEmitter {
return false;
}
const constraints = this.yieldConstraintsFor(strategySelector);
const result =
strategy.getResult(strategySelector.parameters,
context,
constraints,
strategySelector.variants);
const result = strategy.getResult(
strategySelector.parameters,
context,
constraints,
strategySelector.variants,
);

if (result.enabled) {
strategyResult = result;
Expand All @@ -165,7 +168,7 @@ export default class UnleashClient extends EventEmitter {
return strategyResult;
}

* yieldConstraintsFor(
*yieldConstraintsFor(
strategy: StrategyTransportInterface,
): IterableIterator<Constraint | undefined> {
if (strategy.constraints) {
Expand All @@ -178,7 +181,7 @@ export default class UnleashClient extends EventEmitter {
yield* this.yieldSegmentConstraints(segments);
}

* yieldSegmentConstraints(
*yieldSegmentConstraints(
segments: (Segment | undefined)[],
): IterableIterator<Constraint | undefined> {
// eslint-disable-next-line no-restricted-syntax
Expand All @@ -194,7 +197,7 @@ export default class UnleashClient extends EventEmitter {
}
}

getVariant(name: string, context: Context, fallbackVariant?: Variant): VariantWithFeatureStatus {
getVariant(name: string, context: Context, fallbackVariant?: Variant): Variant {
const feature = this.repository.getToggle(name);
const variant = this.resolveVariant(feature, context, true, fallbackVariant);
if (feature?.impressionData) {
Expand All @@ -215,9 +218,7 @@ export default class UnleashClient extends EventEmitter {
// This function is intended to close an issue in the proxy where feature enabled
// state gets checked twice when resolving a variant with random stickiness and
// gradual rollout. This is not intended for general use, prefer getVariant instead
forceGetVariant(name: string,
context: Context,
fallbackVariant?: Variant): VariantWithFeatureStatus {
forceGetVariant(name: string, context: Context, fallbackVariant?: Variant): Variant {
const feature = this.repository.getToggle(name);
return this.resolveVariant(feature, context, true, fallbackVariant);
}
Expand All @@ -227,11 +228,11 @@ export default class UnleashClient extends EventEmitter {
context: Context,
checkToggle: boolean,
fallbackVariant?: Variant,
): VariantWithFeatureStatus {
const fallback = fallbackVariant || getDefaultVariant();
): Variant {
const fallback = fallbackVariant || defaultVariant;

if (typeof feature === 'undefined') {
return { ...fallback, featureEnabled: false };
return { ...fallback, feature_enabled: false };
}

let featureEnabled = !checkToggle;
Expand All @@ -240,30 +241,28 @@ export default class UnleashClient extends EventEmitter {
featureEnabled = result.enabled;

if (result.enabled && result.variant) {
return { ...result.variant, featureEnabled };
return { ...result.variant, feature_enabled: featureEnabled };
}

if (!result.enabled) {
return { ...fallback, featureEnabled };
return { ...fallback, feature_enabled: featureEnabled };
}
}

if (!feature.variants ||
!Array.isArray(feature.variants) ||
feature.variants.length === 0) {
return { ...fallback, featureEnabled };
if (!feature.variants || !Array.isArray(feature.variants) || feature.variants.length === 0) {
return { ...fallback, feature_enabled: featureEnabled };
}

const variant: VariantDefinition | null = selectVariant(feature, context);
if (variant === null) {
return { ...fallback, featureEnabled };
return { ...fallback, feature_enabled: featureEnabled };
}

return {
name: variant.name,
payload: variant.payload,
enabled: true,
featureEnabled,
feature_enabled: featureEnabled,
};
}
}
6 changes: 3 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { once } from 'events';
import { Unleash } from './unleash';
import { Variant, getDefaultVariant, PayloadType } from './variant';
import { Variant, defaultVariant, PayloadType } from './variant';
import { Context } from './context';
import { TagFilter } from './tags';
import { UnleashEvents } from './events';
Expand Down Expand Up @@ -53,7 +53,7 @@ export function getVariant(
context: Context = {},
fallbackVariant?: Variant,
): Variant {
const variant = fallbackVariant || getDefaultVariant();
const variant = fallbackVariant || defaultVariant;
return instance ? instance.getVariant(name, context, variant) : variant;
}

Expand All @@ -62,7 +62,7 @@ export function forceGetVariant(
context: Context = {},
fallbackVariant?: Variant,
): Variant {
const variant = fallbackVariant || getDefaultVariant();
const variant = fallbackVariant || defaultVariant;
return instance ? instance.forceGetVariant(name, context, variant) : variant;
}

Expand Down
8 changes: 4 additions & 4 deletions src/test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ test('should always return defaultVariant if missing variant', (t) => {
const defaultVariant = {
enabled: false,
name: 'disabled',
featureEnabled: true
feature_enabled: true
};
t.deepEqual(result, defaultVariant);

Expand All @@ -259,7 +259,7 @@ test('should always return defaultVariant if missing variant', (t) => {
type: 'string',
value: '',
},
featureEnabled: true
feature_enabled: true
};
const result2 = client.getVariant('feature-but-no-variant', {}, fallback);

Expand Down Expand Up @@ -381,7 +381,7 @@ test('should favor strategy variant over feature variant', (t) => {
name: 'strategyVariantName',
payload: { type: 'string', value: 'strategyVariantValue' },
enabled: true,
featureEnabled: true
feature_enabled: true
},
);
});
Expand Down Expand Up @@ -411,7 +411,7 @@ test('should return disabled variant for non-matching strategy variant', (t) =>
t.deepEqual(variant, {
name: 'disabled',
enabled: false,
featureEnabled: false,
feature_enabled: false,
},
);
});
Expand Down
18 changes: 9 additions & 9 deletions src/test/snapshots/client.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant3',
payload: {
type: 'string',
Expand All @@ -22,7 +22,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant3',
payload: {
type: 'string',
Expand All @@ -34,7 +34,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant3',
payload: {
type: 'string',
Expand All @@ -48,7 +48,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant3',
payload: {
type: 'string',
Expand All @@ -60,7 +60,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant1',
payload: {
type: 'string',
Expand All @@ -72,7 +72,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant3',
payload: {
type: 'string',
Expand All @@ -86,7 +86,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant3',
payload: {
type: 'string',
Expand All @@ -98,7 +98,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant3',
payload: {
type: 'string',
Expand All @@ -110,7 +110,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: true,
featureEnabled: true,
feature_enabled: true,
name: 'variant2',
payload: {
type: 'string',
Expand Down
Binary file modified src/test/snapshots/client.test.ts.snap
Binary file not shown.
1 change: 1 addition & 0 deletions src/test/snapshots/index.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Generated by [AVA](https://avajs.dev).
{
enabled: false,
feature_enabled: false,
name: 'disabled',
}

Expand Down
Binary file modified src/test/snapshots/index.test.ts.snap
Binary file not shown.
Loading

0 comments on commit 51e601b

Please sign in to comment.