1
0
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:
Mateusz Kwasniewski 2025-04-04 14:20:42 +02:00 committed by GitHub
parent bd11ece873
commit 4130e06d17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 148 additions and 1 deletions

View File

@ -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();

View File

@ -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: [] },
],
},
],
});
});

View File

@ -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',

View File

@ -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 = {