1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-04 00:18:40 +01:00
unleash.unleash/src/lib/features/feature-search/feature-search-controller.ts

133 lines
4.5 KiB
TypeScript
Raw Normal View History

import { Response } from 'express';
2023-10-25 10:50:59 +02:00
import Controller from '../../routes/controller';
import { FeatureSearchService, OpenApiService } from '../../services';
2023-10-25 10:50:59 +02:00
import {
IFlagResolver,
IUnleashConfig,
IUnleashServices,
NONE,
} from '../../types';
import { Logger } from '../../logger';
import { createResponseSchema, getStandardResponses } from '../../openapi';
import { IAuthRequest } from '../../routes/unleash-types';
import { InvalidOperationError } from '../../error';
import {
FeatureSearchQueryParameters,
featureSearchQueryParameters,
} from '../../openapi/spec/feature-search-query-parameters';
2023-10-25 10:50:59 +02:00
const PATH = '/features';
type FeatureSearchServices = Pick<
IUnleashServices,
'openApiService' | 'featureSearchService'
>;
2023-10-25 10:50:59 +02:00
export default class FeatureSearchController extends Controller {
private openApiService: OpenApiService;
private flagResolver: IFlagResolver;
private featureSearchService: FeatureSearchService;
2023-10-25 10:50:59 +02:00
private readonly logger: Logger;
constructor(
config: IUnleashConfig,
{ openApiService, featureSearchService }: FeatureSearchServices,
2023-10-25 10:50:59 +02:00
) {
super(config);
this.openApiService = openApiService;
this.flagResolver = config.flagResolver;
this.featureSearchService = featureSearchService;
2023-10-25 10:50:59 +02:00
this.logger = config.getLogger(
'/feature-search/feature-search-controller.ts',
);
this.route({
method: 'get',
path: PATH,
handler: this.searchFeatures,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['Search'],
summary: 'Search and filter features',
description: 'Search and filter by selected fields.',
operationId: 'searchFeatures',
2023-10-26 10:05:47 +02:00
// top level array needs to be mutable according to openapi library
parameters: [...featureSearchQueryParameters],
2023-10-25 10:50:59 +02:00
responses: {
200: createResponseSchema('searchFeaturesSchema'),
...getStandardResponses(401, 403, 404),
},
}),
],
});
}
async searchFeatures(
req: IAuthRequest<any, any, any, FeatureSearchQueryParameters>,
2023-10-25 10:50:59 +02:00
res: Response,
): Promise<void> {
if (this.config.flagResolver.isEnabled('featureSearchAPI')) {
const {
query,
project,
type,
tag,
segment,
createdAt,
state,
status,
offset,
2023-10-31 14:10:31 +01:00
limit = '50',
2023-11-03 13:15:12 +01:00
sortOrder,
sortBy,
favoritesFirst,
} = req.query;
2023-10-26 15:29:30 +02:00
const userId = req.user.id;
const normalizedQuery = query
?.split(',')
.map((query) => query.trim())
.filter((query) => query);
const normalizedStatus = status
?.map((tag) => tag.split(':'))
.filter(
(tag) =>
tag.length === 2 &&
['enabled', 'disabled'].includes(tag[1]),
);
2023-10-31 14:10:31 +01:00
const normalizedLimit =
Number(limit) > 0 && Number(limit) <= 50 ? Number(limit) : 50;
const normalizedOffset = Number(offset) > 0 ? Number(offset) : 0;
2023-11-03 13:15:12 +01:00
const normalizedSortBy: string = sortBy ? sortBy : 'createdAt';
const normalizedSortOrder =
sortOrder === 'asc' || sortOrder === 'desc' ? sortOrder : 'asc';
const normalizedFavoritesFirst = favoritesFirst === 'true';
const { features, total } = await this.featureSearchService.search({
searchParams: normalizedQuery,
project,
type,
userId,
tag,
segment,
state,
createdAt,
status: normalizedStatus,
offset: normalizedOffset,
limit: normalizedLimit,
sortBy: normalizedSortBy,
sortOrder: normalizedSortOrder,
favoritesFirst: normalizedFavoritesFirst,
});
2023-10-31 14:10:31 +01:00
res.json({ features, total });
2023-10-25 10:50:59 +02:00
} else {
throw new InvalidOperationError(
'Feature Search API is not enabled',
);
}
}
}