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:
parent
c4b697b57d
commit
37d6c4886a
@ -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) {
|
||||||
|
@ -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 {
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user