2023-11-08 10:12:42 +01:00
|
|
|
import { Response } from 'express';
|
2023-10-25 10:50:59 +02:00
|
|
|
import Controller from '../../routes/controller';
|
2023-10-25 15:18:52 +02:00
|
|
|
import { FeatureSearchService, OpenApiService } from '../../services';
|
2023-10-25 10:50:59 +02:00
|
|
|
import {
|
|
|
|
IFlagResolver,
|
|
|
|
IUnleashConfig,
|
|
|
|
IUnleashServices,
|
|
|
|
NONE,
|
2023-12-05 10:25:56 +01:00
|
|
|
serializeDates,
|
2023-10-25 10:50:59 +02:00
|
|
|
} from '../../types';
|
|
|
|
import { Logger } from '../../logger';
|
2023-12-05 10:25:56 +01:00
|
|
|
import {
|
|
|
|
createResponseSchema,
|
|
|
|
getStandardResponses,
|
|
|
|
searchFeaturesSchema,
|
|
|
|
} from '../../openapi';
|
2023-10-25 10:50:59 +02:00
|
|
|
import { IAuthRequest } from '../../routes/unleash-types';
|
2023-10-25 16:12:21 +02:00
|
|
|
import {
|
|
|
|
FeatureSearchQueryParameters,
|
|
|
|
featureSearchQueryParameters,
|
|
|
|
} from '../../openapi/spec/feature-search-query-parameters';
|
2023-10-25 10:50:59 +02:00
|
|
|
|
|
|
|
const PATH = '/features';
|
|
|
|
|
2023-10-25 15:18:52 +02:00
|
|
|
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;
|
|
|
|
|
2023-10-25 15:18:52 +02:00
|
|
|
private featureSearchService: FeatureSearchService;
|
|
|
|
|
2023-10-25 10:50:59 +02:00
|
|
|
private readonly logger: Logger;
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
config: IUnleashConfig,
|
2023-10-25 15:18:52 +02:00
|
|
|
{ openApiService, featureSearchService }: FeatureSearchServices,
|
2023-10-25 10:50:59 +02:00
|
|
|
) {
|
|
|
|
super(config);
|
|
|
|
this.openApiService = openApiService;
|
|
|
|
this.flagResolver = config.flagResolver;
|
2023-10-25 15:18:52 +02:00
|
|
|
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(
|
2023-10-25 16:12:21 +02:00
|
|
|
req: IAuthRequest<any, any, any, FeatureSearchQueryParameters>,
|
2023-10-25 10:50:59 +02:00
|
|
|
res: Response,
|
|
|
|
): Promise<void> {
|
2024-01-31 09:01:31 +01:00
|
|
|
const {
|
|
|
|
query,
|
|
|
|
project,
|
|
|
|
type,
|
|
|
|
tag,
|
|
|
|
segment,
|
|
|
|
createdAt,
|
|
|
|
state,
|
|
|
|
status,
|
|
|
|
offset,
|
|
|
|
limit = '50',
|
|
|
|
sortOrder,
|
|
|
|
sortBy,
|
|
|
|
favoritesFirst,
|
|
|
|
} = req.query;
|
|
|
|
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-25 10:50:59 +02:00
|
|
|
);
|
2024-01-31 09:01:31 +01:00
|
|
|
const normalizedLimit =
|
|
|
|
Number(limit) > 0 && Number(limit) <= 100 ? Number(limit) : 25;
|
|
|
|
const normalizedOffset = Number(offset) > 0 ? Number(offset) : 0;
|
|
|
|
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,
|
|
|
|
});
|
|
|
|
|
|
|
|
this.openApiService.respondWithValidation(
|
|
|
|
200,
|
|
|
|
res,
|
|
|
|
searchFeaturesSchema.$id,
|
|
|
|
serializeDates({
|
|
|
|
features,
|
|
|
|
total,
|
|
|
|
}),
|
|
|
|
);
|
2023-10-25 10:50:59 +02:00
|
|
|
}
|
|
|
|
}
|