1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-27 13:49:10 +02:00

Respect sort order when displaying strategies (#943)

This commit is contained in:
Christopher Kolstad 2021-09-17 15:11:17 +02:00 committed by GitHub
parent c4b697b57d
commit 37d6c4886a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 145 additions and 7 deletions

View File

@ -47,6 +47,7 @@ interface IFeatureStrategiesTable {
strategy_name: string; strategy_name: string;
parameters: object; parameters: object;
constraints: string; constraints: string;
sort_order: number;
created_at?: Date; created_at?: Date;
} }
@ -60,6 +61,7 @@ function mapRow(row: IFeatureStrategiesTable): IFeatureStrategy {
parameters: row.parameters, parameters: row.parameters,
constraints: (row.constraints as unknown as IConstraint[]) || [], constraints: (row.constraints as unknown as IConstraint[]) || [],
createdAt: row.created_at, createdAt: row.created_at,
sortOrder: row.sort_order,
}; };
} }
@ -73,6 +75,7 @@ function mapInput(input: IFeatureStrategy): IFeatureStrategiesTable {
parameters: input.parameters, parameters: input.parameters,
constraints: JSON.stringify(input.constraints || []), constraints: JSON.stringify(input.constraints || []),
created_at: input.createdAt, created_at: input.createdAt,
sort_order: input.sortOrder,
}; };
} }
@ -179,13 +182,13 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
environment: string, environment: string,
): Promise<IFeatureStrategy[]> { ): Promise<IFeatureStrategy[]> {
const stopTimer = this.timer('getForFeature'); const stopTimer = this.timer('getForFeature');
const rows = await this.db<IFeatureStrategiesTable>( const rows = await this.db<IFeatureStrategiesTable>(T.featureStrategies)
T.featureStrategies, .where({
).where({
project_name: projectId, project_name: projectId,
feature_name: featureName, feature_name: featureName,
environment, environment,
}); })
.orderBy('sort_order', 'asc');
stopTimer(); stopTimer();
return rows.map(mapRow); return rows.map(mapRow);
} }
@ -211,6 +214,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
'feature_strategies.strategy_name as strategy_name', 'feature_strategies.strategy_name as strategy_name',
'feature_strategies.parameters as parameters', 'feature_strategies.parameters as parameters',
'feature_strategies.constraints as constraints', 'feature_strategies.constraints as constraints',
'feature_strategies.sort_order as sort_order',
) )
.fullOuterJoin( .fullOuterJoin(
'feature_environments', 'feature_environments',
@ -262,6 +266,12 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
featureToggle.environments = Object.values( featureToggle.environments = Object.values(
featureToggle.environments, featureToggle.environments,
); );
featureToggle.environments = featureToggle.environments.map((e) => {
e.strategies = e.strategies.sort(
(a, b) => a.sortOrder - b.sortOrder,
);
return e;
});
featureToggle.archived = archived; featureToggle.archived = archived;
return featureToggle; return featureToggle;
} }
@ -359,6 +369,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
name: r.strategy_name, name: r.strategy_name,
constraints: r.constraints || [], constraints: r.constraints || [],
parameters: r.parameters, parameters: r.parameters,
sortOrder: r.sort_order,
id: r.strategy_id, id: r.strategy_id,
}; };
if (!includeId) { if (!includeId) {

View File

@ -22,6 +22,7 @@ interface FeatureStrategyParams {
projectId: string; projectId: string;
featureName: string; featureName: string;
environment: string; environment: string;
sortOrder?: number;
} }
interface FeatureParams extends ProjectParam { interface FeatureParams extends ProjectParam {

View File

@ -106,6 +106,7 @@ class FeatureToggleServiceV2 {
strategyName: strategyConfig.name, strategyName: strategyConfig.name,
constraints: strategyConfig.constraints, constraints: strategyConfig.constraints,
parameters: strategyConfig.parameters, parameters: strategyConfig.parameters,
sortOrder: strategyConfig.sortOrder,
projectId, projectId,
featureName, featureName,
environment, environment,
@ -212,6 +213,7 @@ class FeatureToggleServiceV2 {
name: strat.strategyName, name: strat.strategyName,
constraints: strat.constraints, constraints: strat.constraints,
parameters: strat.parameters, parameters: strat.parameters,
sortOrder: strat.sortOrder,
})); }));
} }
throw new NotFoundError( throw new NotFoundError(

View File

@ -14,6 +14,7 @@ export interface IStrategyConfig {
name: string; name: string;
constraints: IConstraint[]; constraints: IConstraint[];
parameters: Object; parameters: Object;
sortOrder?: number;
} }
export interface IFeatureStrategy { export interface IFeatureStrategy {
id: string; id: string;
@ -22,6 +23,7 @@ export interface IFeatureStrategy {
environment: string; environment: string;
strategyName: string; strategyName: string;
parameters: object; parameters: object;
sortOrder?: number;
constraints: IConstraint[]; constraints: IConstraint[];
createdAt?: Date; createdAt?: Date;
} }

View File

@ -4,6 +4,9 @@ import getLogger from '../../../../fixtures/no-logger';
let app: IUnleashTest; let app: IUnleashTest;
let db: ITestDb; let db: ITestDb;
const sortOrderFirst = 0;
const sortOrderSecond = 10;
const sortOrderDefault = 9999;
beforeAll(async () => { beforeAll(async () => {
db = await dbInit('feature_strategy_api_serial', getLogger); db = await dbInit('feature_strategy_api_serial', getLogger);
@ -31,6 +34,44 @@ afterAll(async () => {
await db.destroy(); await db.destroy();
}); });
async function addStrategies(featureName: string, envName: string) {
await app.request
.post(
`/api/admin/projects/default/features/${featureName}/environments/${envName}/strategies`,
)
.send({
name: 'default',
parameters: {
userId: 'string',
},
})
.expect(200);
await app.request
.post(
`/api/admin/projects/default/features/${featureName}/environments/${envName}/strategies`,
)
.send({
name: 'default',
parameters: {
userId: 'string',
},
sortOrder: sortOrderFirst,
})
.expect(200);
await app.request
.post(
`/api/admin/projects/default/features/${featureName}/environments/${envName}/strategies`,
)
.send({
name: 'default',
parameters: {
userId: 'string',
},
sortOrder: sortOrderSecond,
})
.expect(200);
}
test('Trying to add a strategy configuration to environment not connected to toggle should fail', async () => { test('Trying to add a strategy configuration to environment not connected to toggle should fail', async () => {
await app.request await app.request
.post('/api/admin/features') .post('/api/admin/features')
@ -842,3 +883,84 @@ test('Can delete strategy from feature toggle', async () => {
) )
.expect(200); .expect(200);
}); });
test('List of strategies should respect sortOrder', async () => {
const envName = 'sortOrderdel-strategy';
const featureName = 'feature.sort.order.one';
// Create environment
await db.stores.environmentStore.create({
name: envName,
displayName: 'Enable feature for environment',
type: 'test',
});
// Connect environment to project
await app.request
.post('/api/admin/projects/default/environments')
.send({
environment: envName,
})
.expect(200);
await app.request
.post('/api/admin/projects/default/features')
.send({ name: featureName })
.expect(201);
await addStrategies(featureName, envName);
const { body } = await app.request.get(
`/api/admin/projects/default/features/${featureName}/environments/${envName}/strategies`,
);
const strategies = body;
expect(strategies[0].sortOrder).toBe(sortOrderFirst);
expect(strategies[1].sortOrder).toBe(sortOrderSecond);
expect(strategies[2].sortOrder).toBe(sortOrderDefault);
});
test('Feature strategies list should respect strategy sortorders for each environment', async () => {
const envName = 'sort-order-within-environment-one';
const secondEnv = 'sort-order-within-environment-two';
const featureName = 'feature.sort.order.environment.list';
// Create environment
await db.stores.environmentStore.create({
name: envName,
displayName: 'Sort orders within environment',
type: 'test',
});
await db.stores.environmentStore.create({
name: secondEnv,
displayName: 'Sort orders within environment',
type: 'test',
});
// Connect environment to project
await app.request
.post('/api/admin/projects/default/environments')
.send({
environment: envName,
})
.expect(200);
await app.request
.post('/api/admin/projects/default/environments')
.send({
environment: secondEnv,
})
.expect(200);
await app.request
.post('/api/admin/projects/default/features')
.send({ name: featureName })
.expect(201);
await addStrategies(featureName, envName);
await addStrategies(featureName, secondEnv);
const response = await app.request.get(
`/api/admin/projects/default/features/${featureName}`,
);
const { body } = response;
let { strategies } = body.environments.find((e) => e.name === envName);
expect(strategies[0].sortOrder).toBe(sortOrderFirst);
expect(strategies[1].sortOrder).toBe(sortOrderSecond);
expect(strategies[2].sortOrder).toBe(sortOrderDefault);
strategies = body.environments.find((e) => e.name === secondEnv).strategies;
expect(strategies[0].sortOrder).toBe(sortOrderFirst);
expect(strategies[1].sortOrder).toBe(sortOrderSecond);
expect(strategies[2].sortOrder).toBe(sortOrderDefault);
});