mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: flag overview change requests (#9702)
This commit is contained in:
parent
bd11ece873
commit
4130e06d17
@ -71,6 +71,7 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
||||
hasEnabledStrategies: r.has_enabled_strategies,
|
||||
yes: Number(r.yes) || 0,
|
||||
no: Number(r.no) || 0,
|
||||
changeRequestIds: r.change_request_ids ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -326,6 +327,43 @@ class FeatureSearchStore implements IFeatureSearchStore {
|
||||
'ranked_features.feature_name',
|
||||
'lifecycle.stage_feature',
|
||||
);
|
||||
if (this.flagResolver.isEnabled('flagsOverviewSearch')) {
|
||||
finalQuery
|
||||
.leftJoin(
|
||||
this.db('change_request_events AS cre')
|
||||
.join(
|
||||
'change_requests AS cr',
|
||||
'cre.change_request_id',
|
||||
'cr.id',
|
||||
)
|
||||
.select('cre.feature')
|
||||
.select(
|
||||
this.db.raw(
|
||||
'array_agg(distinct cre.change_request_id) AS change_request_ids',
|
||||
),
|
||||
)
|
||||
.select('cr.environment')
|
||||
.groupBy('cre.feature', 'cr.environment')
|
||||
.whereNotIn('cr.state', [
|
||||
'Applied',
|
||||
'Cancelled',
|
||||
'Rejected',
|
||||
])
|
||||
.as('feature_cr'),
|
||||
function () {
|
||||
this.on(
|
||||
'feature_cr.feature',
|
||||
'=',
|
||||
'ranked_features.feature_name',
|
||||
).andOn(
|
||||
'feature_cr.environment',
|
||||
'=',
|
||||
'ranked_features.environment',
|
||||
);
|
||||
},
|
||||
)
|
||||
.select('feature_cr.change_request_ids');
|
||||
}
|
||||
this.queryExtraData(finalQuery);
|
||||
const rows = await finalQuery;
|
||||
stopTimer();
|
||||
|
@ -29,6 +29,7 @@ beforeAll(async () => {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
anonymiseEventLog: true,
|
||||
flagsOverviewSearch: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -1286,3 +1287,98 @@ test('should return tags with color information from tag type', async () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
const createChangeRequest = async ({
|
||||
id,
|
||||
feature,
|
||||
environment,
|
||||
state,
|
||||
}: { id: number; feature: string; environment: string; state: string }) => {
|
||||
await db
|
||||
.rawDatabase('change_requests')
|
||||
.insert({ id, environment, state, project: 'default', created_by: 1 });
|
||||
await db.rawDatabase('change_request_events').insert({
|
||||
id,
|
||||
feature,
|
||||
action: 'updateEnabled',
|
||||
created_by: 1,
|
||||
change_request_id: id,
|
||||
});
|
||||
};
|
||||
|
||||
test('should return change request ids per environment', async () => {
|
||||
await app.createFeature('my_feature_a');
|
||||
await app.createFeature('my_feature_b');
|
||||
|
||||
await createChangeRequest({
|
||||
id: 1,
|
||||
feature: 'my_feature_a',
|
||||
environment: 'production',
|
||||
state: 'In review',
|
||||
});
|
||||
await createChangeRequest({
|
||||
id: 2,
|
||||
feature: 'my_feature_a',
|
||||
environment: 'production',
|
||||
state: 'Applied',
|
||||
});
|
||||
await createChangeRequest({
|
||||
id: 3,
|
||||
feature: 'my_feature_a',
|
||||
environment: 'production',
|
||||
state: 'Cancelled',
|
||||
});
|
||||
await createChangeRequest({
|
||||
id: 4,
|
||||
feature: 'my_feature_a',
|
||||
environment: 'production',
|
||||
state: 'Rejected',
|
||||
});
|
||||
await createChangeRequest({
|
||||
id: 5,
|
||||
feature: 'my_feature_a',
|
||||
environment: 'development',
|
||||
state: 'Draft',
|
||||
});
|
||||
await createChangeRequest({
|
||||
id: 6,
|
||||
feature: 'my_feature_a',
|
||||
environment: 'development',
|
||||
state: 'Scheduled',
|
||||
});
|
||||
await createChangeRequest({
|
||||
id: 7,
|
||||
feature: 'my_feature_a',
|
||||
environment: 'development',
|
||||
state: 'Approved',
|
||||
});
|
||||
await createChangeRequest({
|
||||
id: 8,
|
||||
feature: 'my_feature_b',
|
||||
environment: 'development',
|
||||
state: 'Approved',
|
||||
});
|
||||
|
||||
const { body } = await searchFeatures({});
|
||||
|
||||
expect(body).toMatchObject({
|
||||
features: [
|
||||
{
|
||||
name: 'my_feature_a',
|
||||
environments: [
|
||||
{ name: 'default', changeRequestIds: [] },
|
||||
{ name: 'development', changeRequestIds: [5, 6, 7] },
|
||||
{ name: 'production', changeRequestIds: [1] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'my_feature_b',
|
||||
environments: [
|
||||
{ name: 'default', changeRequestIds: [] },
|
||||
{ name: 'development', changeRequestIds: [8] },
|
||||
{ name: 'production', changeRequestIds: [] },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
@ -63,6 +63,14 @@ export const featureEnvironmentSchema = {
|
||||
},
|
||||
description: 'A list of variants for the feature environment',
|
||||
},
|
||||
changeRequestIds: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'number',
|
||||
},
|
||||
description:
|
||||
'Experimental. A list of change request identifiers for actionable change requests that are not Cancelled, Rejected or Approved.',
|
||||
},
|
||||
lastSeenAt: {
|
||||
type: 'string',
|
||||
format: 'date-time',
|
||||
|
@ -66,7 +66,8 @@ export type IFlagKey =
|
||||
| 'tagTypeColor'
|
||||
| 'globalChangeRequestConfig'
|
||||
| 'addEditStrategy'
|
||||
| 'newStrategyDropdown';
|
||||
| 'newStrategyDropdown'
|
||||
| 'flagsOverviewSearch';
|
||||
|
||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||
|
||||
@ -319,6 +320,10 @@ const flags: IFlags = {
|
||||
process.env.UNLEASH_EXPERIMENTAL_NEW_STRATEGY_DROPDOWN,
|
||||
false,
|
||||
),
|
||||
flagsOverviewSearch: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_FLAGS_OVERVIEW_SEARCH,
|
||||
false,
|
||||
),
|
||||
};
|
||||
|
||||
export const defaultExperimentalOptions: IExperimentalOptions = {
|
||||
|
Loading…
Reference in New Issue
Block a user