Skip to content

Commit

Permalink
feat: add option to return disabled strategies (#5059)
Browse files Browse the repository at this point in the history
Adds the option to include disabled strategies (behind the
playgroundImprovements flag

Closes #
[1-1505](https://linear.app/unleash/issue/1-1505/return-disabled-strategies-in-the-playground-features-request)

---------

Signed-off-by: andreas-unleash <[email protected]>
  • Loading branch information
andreas-unleash authored Oct 17, 2023
1 parent 04568eb commit cf42a82
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export class FeatureToggleRowConverter {
constraints: row.constraints || [],
parameters: mapValues(row.parameters || {}, ensureStringValue),
sortOrder: row.sort_order,
disabled: row.strategy_disabled,
};
strategy.variants = row.strategy_variants || [];
return strategy;
Expand Down Expand Up @@ -111,6 +112,7 @@ export class FeatureToggleRowConverter {
row: any,
feature: PartialDeep<IFeatureToggleClient>,
featureQuery?: IFeatureToggleQuery,
includeDisabledStrategies?: boolean,
) => {
feature.impressionData = row.impression_data;
feature.enabled = !!row.enabled;
Expand All @@ -123,7 +125,10 @@ export class FeatureToggleRowConverter {
feature.variants = row.variants || [];
feature.project = row.project;

if (this.isUnseenStrategyRow(feature, row) && !row.strategy_disabled) {
if (
this.isUnseenStrategyRow(feature, row) &&
(includeDisabledStrategies ? true : !row.strategy_disabled)
) {
feature.strategies?.push(this.rowToStrategy(row));
}
if (this.isNewTag(feature, row)) {
Expand All @@ -141,13 +146,19 @@ export class FeatureToggleRowConverter {
buildFeatureToggleListFromRows = (
rows: any[],
featureQuery?: IFeatureToggleQuery,
includeDisabledStrategies?: boolean,
): FeatureToggle[] => {
const result = rows.reduce((acc, r) => {
let feature: PartialDeep<IFeatureToggleClient> = acc[r.name] ?? {
strategies: [],
};

feature = this.createBaseFeature(r, feature, featureQuery);
feature = this.createBaseFeature(
r,
feature,
featureQuery,
includeDisabledStrategies,
);

feature.createdAt = r.created_at;
feature.favorite = r.favorite;
Expand All @@ -162,14 +173,20 @@ export class FeatureToggleRowConverter {
buildPlaygroundFeaturesFromRows = (
rows: any[],
dependentFeaturesEnabled: boolean,
includeDisabledStrategies: boolean,
featureQuery?: IFeatureToggleQuery,
): FeatureConfigurationClient[] => {
const result = rows.reduce((acc, r) => {
let feature: PartialDeep<IFeatureToggleClient> = acc[r.name] ?? {
strategies: [],
};

feature = this.createBaseFeature(r, feature, featureQuery);
feature = this.createBaseFeature(
r,
feature,
featureQuery,
includeDisabledStrategies,
);

if (r.parent && dependentFeaturesEnabled) {
feature.dependencies = feature.dependencies || [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export default class FakeFeatureToggleStore implements IFeatureToggleStore {

async getPlaygroundFeatures(
dependentFeaturesEnabled: boolean,
includeDisabledStrategies: boolean,
query?: IFeatureToggleQuery,
): Promise<FeatureConfigurationClient[]> {
return this.features.filter(
Expand Down
1 change: 1 addition & 0 deletions src/lib/features/feature-toggle/feature-toggle-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,7 @@ class FeatureToggleService {
await this.clientFeatureToggleStore.getPlayground(query || {}),
await this.featureToggleStore.getPlaygroundFeatures(
this.flagResolver.isEnabled('dependentFeatures'),
this.flagResolver.isEnabled('playgroundImprovements'),
query,
),
]);
Expand Down
4 changes: 4 additions & 0 deletions src/lib/features/feature-toggle/feature-toggle-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
featureQuery?: IFeatureToggleQuery,
userId?: number,
archived: boolean = false,
includeDisabledStrategies: boolean = false,
): Promise<FeatureToggle[]> {
const environment = featureQuery?.environment || DEFAULT_ENV;

Expand Down Expand Up @@ -150,11 +151,13 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
return this.featureToggleRowConverter.buildFeatureToggleListFromRows(
rows,
featureQuery,
includeDisabledStrategies,
);
}

async getPlaygroundFeatures(
dependentFeaturesEnabled: boolean,
includeDisabledStrategies: boolean,
featureQuery: IFeatureToggleQuery,
): Promise<FeatureConfigurationClient[]> {
const environment = featureQuery?.environment || DEFAULT_ENV;
Expand All @@ -177,6 +180,7 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
return this.featureToggleRowConverter.buildPlaygroundFeaturesFromRows(
rows,
dependentFeaturesEnabled,
includeDisabledStrategies,
featureQuery,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface IFeatureToggleStore extends Store<FeatureToggle, string> {
): Promise<FeatureToggle[]>;
getPlaygroundFeatures(
dependentFeaturesEnabled: boolean,
includeDisabledStrategies: boolean,
featureQuery?: IFeatureToggleQuery,
): Promise<FeatureConfigurationClient[]>;
countByDate(queryModifiers: {
Expand Down
46 changes: 45 additions & 1 deletion src/lib/features/playground/advanced-playground.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ let db: ITestDb;

beforeAll(async () => {
db = await dbInit('advanced_playground', getLogger, {
experimental: { flags: { dependentFeatures: true } },
experimental: {
flags: { dependentFeatures: true, playgroundImprovements: true },
},
});
app = await setupAppWithCustomConfig(
db.stores,
Expand All @@ -23,6 +25,9 @@ beforeAll(async () => {
strategyVariant: true,
privateProjects: true,
dependentFeatures: true,
playgroundImprovements: true,
useLastSeenRefactor: true,
separateAdminClientApi: true,
},
},
},
Expand Down Expand Up @@ -327,6 +332,7 @@ test('show matching variant from variants selection only for enabled toggles', a
variants,
},
);

await enableToggle('test-playground-feature-with-variants');

const { body: result } = await app.request
Expand Down Expand Up @@ -357,3 +363,41 @@ test('show matching variant from variants selection only for enabled toggles', a
expect(feature.variants).toMatchObject([]);
});
});

test('should return disabled strategies with unevaluated result', async () => {
await createFeatureToggleWithStrategy(
'test-playground-feature-with-disabled-strategy',
{
name: 'flexibleRollout',
constraints: [],
disabled: true,
parameters: {
rollout: '50',
stickiness: 'random',
groupId: 'test-playground-feature-with-variants',
},
},
);

const { body: result } = await app.request
.post('/api/admin/playground/advanced')
.send({
environments: ['default'],
projects: ['default'],
context: { appName: 'playground' },
})
.set('Content-Type', 'application/json')
.expect(200);

const typedResult: AdvancedPlaygroundResponseSchema = result;

const feature = typedResult.features.find(
(feature) =>
feature.name === 'test-playground-feature-with-disabled-strategy',
);

expect(
feature?.environments.default[0].strategies.data[0].result
.evaluationStatus,
).toBe('unevaluated');
});
1 change: 0 additions & 1 deletion src/lib/features/playground/feature-evaluator/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ export default class UnleashClient {
!strategy ||
!strategy.name ||
typeof strategy.name !== 'string' ||
!strategy.isEnabled ||
typeof strategy.isEnabled !== 'function'
) {
throw new Error('Invalid strategy data / interface');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,20 @@ export class Strategy {
}
: undefined;

if (disabled) {
return {
result: {
enabled: 'unknown',
evaluationStatus: 'unevaluated',
},
constraints: constraintResults.constraints,
segments: segmentResults.segments,
};
}

return {
result: {
enabled: disabled ? false : overallResult,
enabled: overallResult,
evaluationStatus: 'complete',
variant,
variants: variant ? variantDefinitions : undefined,
Expand Down
8 changes: 6 additions & 2 deletions src/lib/openapi/spec/playground-strategy-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { overrideSchema } from './override-schema';
export const playgroundStrategyEvaluation = {
evaluationComplete: 'complete',
evaluationIncomplete: 'incomplete',
unevaluated: 'unevaluated',
unknownResult: 'unknown',
} as const;

Expand All @@ -21,8 +22,11 @@ export const strategyEvaluationResults = {
evaluationStatus: {
type: 'string',
description:
"Signals that this strategy could not be evaluated. This is most likely because you're using a custom strategy that Unleash doesn't know about.",
enum: [playgroundStrategyEvaluation.evaluationIncomplete],
"Signals that this strategy could not be evaluated. This is most likely because you're using a custom strategy that Unleash doesn't know about. The `unevaluated` result is also returned if the strategy is disabled.",
enum: [
playgroundStrategyEvaluation.evaluationIncomplete,
playgroundStrategyEvaluation.unevaluated,
],
},
enabled: {
description:
Expand Down
1 change: 1 addition & 0 deletions src/server-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ process.nextTick(async () => {
dependentFeatures: true,
useLastSeenRefactor: true,
separateAdminClientApi: true,
playgroundImprovements: true,
},
},
authentication: {
Expand Down

0 comments on commit cf42a82

Please sign in to comment.