1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: filter by environment status (#5165)

This commit is contained in:
Mateusz Kwasniewski 2023-10-27 08:54:03 +02:00 committed by GitHub
parent 46d7cb236d
commit 1c8fab63e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 9 deletions

View File

@ -72,17 +72,25 @@ export default class FeatureSearchController extends Controller {
res: Response,
): Promise<void> {
if (this.config.flagResolver.isEnabled('featureSearchAPI')) {
const { query, projectId, type, tag } = req.query;
const { query, projectId, type, tag, status } = req.query;
const userId = req.user.id;
const normalizedTag = tag
?.map((tag) => tag.split(':'))
.filter((tag) => tag.length === 2);
const normalizedStatus = status
?.map((tag) => tag.split(':'))
.filter(
(tag) =>
tag.length === 2 &&
['enabled', 'disabled'].includes(tag[1]),
);
const features = await this.featureSearchService.search({
query,
projectId,
type,
userId,
tag: normalizedTag,
status: normalizedStatus,
});
res.json({ features });
} else {

View File

@ -20,13 +20,9 @@ export class FeatureSearchService {
}
async search(params: IFeatureSearchParams) {
const features = await this.featureStrategiesStore.searchFeatures({
projectId: params.projectId,
query: params.query,
userId: params.userId,
type: params.type,
tag: params.tag,
});
const features = await this.featureStrategiesStore.searchFeatures(
params,
);
return features;
}

View File

@ -57,6 +57,18 @@ const filterFeaturesByTag = async (tags: string[], expectedCode = 200) => {
.expect(expectedCode);
};
const filterFeaturesByEnvironmentStatus = async (
environmentStatuses: string[],
expectedCode = 200,
) => {
const statuses = environmentStatuses
.map((status) => `status[]=${status}`)
.join('&');
return app.request
.get(`/api/admin/search/features?${statuses}`)
.expect(expectedCode);
};
const searchFeaturesWithoutQueryParams = async (expectedCode = 200) => {
return app.request.get(`/api/admin/search/features`).expect(expectedCode);
};
@ -99,6 +111,22 @@ test('should filter features by tag', async () => {
});
});
test('should filter features by environment status', async () => {
await app.createFeature('my_feature_a');
await app.createFeature('my_feature_b');
await app.enableFeature('my_feature_a', 'default');
const { body } = await filterFeaturesByEnvironmentStatus([
'default:enabled',
'nonexistentEnv:disabled',
'default:wrongStatus',
]);
expect(body).toMatchObject({
features: [{ name: 'my_feature_a' }],
});
});
test('filter with invalid tag should ignore filter', async () => {
await app.createFeature('my_feature_a');
await app.createFeature('my_feature_b');

View File

@ -522,6 +522,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
query: queryString,
type,
tag,
status,
}: IFeatureSearchParams): Promise<IFeatureOverview[]> {
let query = this.db('features');
if (projectId) {
@ -553,6 +554,23 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
if (type) {
query = query.whereIn('features.type', type);
}
if (status && status.length > 0) {
query = query.where((builder) => {
for (const [envName, envStatus] of status) {
builder.orWhere(function () {
this.where(
'feature_environments.environment',
envName,
).andWhere(
'feature_environments.enabled',
envStatus === 'enabled' ? true : false,
);
});
}
});
}
query = query
.modify(FeatureToggleStore.filterByArchived, false)
.leftJoin(

View File

@ -27,6 +27,7 @@ export interface IFeatureSearchParams {
projectId?: string;
type?: string[];
tag?: string[][];
status?: string[][];
}
export interface IFeatureStrategiesStore

View File

@ -41,7 +41,20 @@ export const featureSearchQueryParameters = [
},
},
description:
'The list of feature tags to filter by. Feature tag has to specify type and value joined with a colon.',
'The list of feature tags to filter by. Feature tag has to specify a type and a value joined with a colon.',
in: 'query',
},
{
name: 'status',
schema: {
type: 'array',
items: {
type: 'string',
example: 'production:enabled',
},
},
description:
'The list of feature environment status to filter by. Feature environment has to specify a name and a status joined with a colon.',
in: 'query',
},
] as const;

View File

@ -47,6 +47,13 @@ export interface IUnleashHttpAPI {
expectedResponseCode?: number,
): supertest.Test;
enableFeature(
feature: string,
environment: string,
project?: string,
expectedResponseCode?: number,
): supertest.Test;
getFeatures(name?: string, expectedResponseCode?: number): supertest.Test;
getProjectFeatures(
@ -219,6 +226,19 @@ function httpApis(
.set('Content-Type', 'application/json')
.expect(expectedResponseCode);
},
enableFeature(
feature: string,
environment,
project = 'default',
expectedResponseCode = 200,
): supertest.Test {
return request
.post(
`/api/admin/projects/${project}/features/${feature}/environments/${environment}/on`,
)
.expect(expectedResponseCode);
},
};
}