mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: created date operators for search (#5513)
1. Added operators for created date 2. Added better descriptions for searchable fields
This commit is contained in:
parent
44d85c0dcd
commit
feae69643c
@ -77,6 +77,7 @@ export default class FeatureSearchController extends Controller {
|
||||
type,
|
||||
tag,
|
||||
segment,
|
||||
createdAt,
|
||||
state,
|
||||
status,
|
||||
offset,
|
||||
@ -112,6 +113,7 @@ export default class FeatureSearchController extends Controller {
|
||||
tag,
|
||||
segment,
|
||||
state,
|
||||
createdAt,
|
||||
status: normalizedStatus,
|
||||
offset: normalizedOffset,
|
||||
limit: normalizedLimit,
|
||||
|
@ -43,7 +43,7 @@ export class FeatureSearchService {
|
||||
|
||||
parseOperatorValue = (field: string, value: string): IQueryParam | null => {
|
||||
const pattern =
|
||||
/^(IS|IS_NOT|IS_ANY_OF|IS_NOT_ANY_OF|INCLUDE|DO_NOT_INCLUDE|INCLUDE_ALL_OF|INCLUDE_ANY_OF|EXCLUDE_IF_ANY_OF|EXCLUDE_ALL):(.+)$/;
|
||||
/^(IS|IS_NOT|IS_ANY_OF|IS_NOT_ANY_OF|INCLUDE|DO_NOT_INCLUDE|INCLUDE_ALL_OF|INCLUDE_ANY_OF|EXCLUDE_IF_ANY_OF|EXCLUDE_ALL|IS_BEFORE|IS_ON_OR_AFTER):(.+)$/;
|
||||
const match = value.match(pattern);
|
||||
|
||||
if (match) {
|
||||
@ -70,6 +70,14 @@ export class FeatureSearchService {
|
||||
}
|
||||
}
|
||||
|
||||
if (params.createdAt) {
|
||||
const parsed = this.parseOperatorValue(
|
||||
'features.created_at',
|
||||
params.createdAt,
|
||||
);
|
||||
if (parsed) queryParams.push(parsed);
|
||||
}
|
||||
|
||||
['tag', 'segment', 'project'].forEach((field) => {
|
||||
if (params[field]) {
|
||||
const parsed = this.parseOperatorValue(field, params[field]);
|
||||
|
@ -126,6 +126,15 @@ const filterFeaturesByState = async (state: string, expectedCode = 200) => {
|
||||
.expect(expectedCode);
|
||||
};
|
||||
|
||||
const filterFeaturesByCreated = async (
|
||||
createdAt: string,
|
||||
expectedCode = 200,
|
||||
) => {
|
||||
return app.request
|
||||
.get(`/api/admin/search/features?createdAt=${createdAt}`)
|
||||
.expect(expectedCode);
|
||||
};
|
||||
|
||||
const filterFeaturesByEnvironmentStatus = async (
|
||||
environmentStatuses: string[],
|
||||
expectedCode = 200,
|
||||
@ -796,3 +805,28 @@ test('should search features by state with operators', async () => {
|
||||
features: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('should search features by created date with operators', async () => {
|
||||
await app.createFeature({
|
||||
name: 'my_feature_a',
|
||||
createdAt: '2023-01-27T15:21:39.975Z',
|
||||
});
|
||||
await app.createFeature({
|
||||
name: 'my_feature_b',
|
||||
createdAt: '2023-01-29T15:21:39.975Z',
|
||||
});
|
||||
|
||||
const { body } = await filterFeaturesByCreated(
|
||||
'IS_BEFORE:2023-01-28T15:21:39.975Z',
|
||||
);
|
||||
expect(body).toMatchObject({
|
||||
features: [{ name: 'my_feature_a' }],
|
||||
});
|
||||
|
||||
const { body: afterBody } = await filterFeaturesByCreated(
|
||||
'IS_ON_OR_AFTER:2023-01-28T15:21:39.975Z',
|
||||
);
|
||||
expect(afterBody).toMatchObject({
|
||||
features: [{ name: 'my_feature_b' }],
|
||||
});
|
||||
});
|
||||
|
@ -1131,6 +1131,12 @@ const applyGenericQueryParams = (
|
||||
case 'IS_NOT_ANY_OF':
|
||||
query.whereNotIn(param.field, param.values);
|
||||
break;
|
||||
case 'IS_BEFORE':
|
||||
query.where(param.field, '<', param.values[0]);
|
||||
break;
|
||||
case 'IS_ON_OR_AFTER':
|
||||
query.where(param.field, '>=', param.values[0]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -26,6 +26,7 @@ export interface IFeatureSearchParams {
|
||||
searchParams?: string[];
|
||||
project?: string;
|
||||
segment?: string;
|
||||
createdAt?: string;
|
||||
state?: string;
|
||||
type?: string[];
|
||||
tag?: string;
|
||||
@ -47,7 +48,9 @@ export type IQueryOperator =
|
||||
| 'INCLUDE_ALL_OF'
|
||||
| 'INCLUDE_ANY_OF'
|
||||
| 'EXCLUDE_IF_ANY_OF'
|
||||
| 'EXCLUDE_ALL';
|
||||
| 'EXCLUDE_ALL'
|
||||
| 'IS_BEFORE'
|
||||
| 'IS_ON_OR_AFTER';
|
||||
|
||||
export interface IQueryParam {
|
||||
field: string;
|
||||
@ -60,53 +63,71 @@ export interface IFeatureStrategiesStore
|
||||
createStrategyFeatureEnv(
|
||||
strategyConfig: Omit<IFeatureStrategy, 'id' | 'createdAt'>,
|
||||
): Promise<IFeatureStrategy>;
|
||||
|
||||
removeAllStrategiesForFeatureEnv(
|
||||
featureName: string,
|
||||
environment: string,
|
||||
): Promise<void>;
|
||||
|
||||
getStrategiesForFeatureEnv(
|
||||
projectId: string,
|
||||
featureName: string,
|
||||
environment: string,
|
||||
): Promise<IFeatureStrategy[]>;
|
||||
|
||||
getFeatureToggleWithEnvs(
|
||||
featureName: string,
|
||||
userId?: number,
|
||||
archived?: boolean,
|
||||
): Promise<FeatureToggleWithEnvironment>;
|
||||
|
||||
getFeatureToggleWithVariantEnvs(
|
||||
featureName: string,
|
||||
userId?: number,
|
||||
archived?,
|
||||
): Promise<FeatureToggleWithEnvironment>;
|
||||
|
||||
getFeatureOverview(
|
||||
params: IFeatureProjectUserParams,
|
||||
): Promise<IFeatureOverview[]>;
|
||||
|
||||
searchFeatures(
|
||||
params: IFeatureSearchParams,
|
||||
queryParams: IQueryParam[],
|
||||
): Promise<{ features: IFeatureOverview[]; total: number }>;
|
||||
): Promise<{
|
||||
features: IFeatureOverview[];
|
||||
total: number;
|
||||
}>;
|
||||
|
||||
getStrategyById(id: string): Promise<IFeatureStrategy>;
|
||||
|
||||
updateStrategy(
|
||||
id: string,
|
||||
updates: Partial<IFeatureStrategy>,
|
||||
): Promise<IFeatureStrategy>;
|
||||
|
||||
deleteConfigurationsForProjectAndEnvironment(
|
||||
projectId: String,
|
||||
environment: String,
|
||||
): Promise<void>;
|
||||
|
||||
setProjectForStrategiesBelongingToFeature(
|
||||
featureName: string,
|
||||
newProjectId: string,
|
||||
): Promise<void>;
|
||||
|
||||
getStrategiesBySegment(segmentId: number): Promise<IFeatureStrategy[]>;
|
||||
|
||||
getStrategiesByContextField(
|
||||
contextFieldName: string,
|
||||
): Promise<IFeatureStrategy[]>;
|
||||
|
||||
updateSortOrder(id: string, sortOrder: number): Promise<void>;
|
||||
|
||||
getAllByFeatures(
|
||||
features: string[],
|
||||
environment?: string,
|
||||
): Promise<IFeatureStrategy[]>;
|
||||
|
||||
getCustomStrategiesInUseCount(): Promise<number>;
|
||||
}
|
||||
|
@ -18,7 +18,8 @@ export const featureSearchQueryParameters = [
|
||||
pattern:
|
||||
'^(IS|IS_NOT|IS_ANY_OF|IS_NOT_ANY_OF):(.*?)(,([a-zA-Z0-9_]+))*$',
|
||||
},
|
||||
description: 'Id of the project where search and filter is performed',
|
||||
description:
|
||||
'Id of the project where search and filter is performed. The project id can be specified with an operator. The supported operators are IS, IS_NOT, IS_ANY_OF, IS_NOT_ANY_OF.',
|
||||
in: 'query',
|
||||
},
|
||||
{
|
||||
@ -29,7 +30,8 @@ export const featureSearchQueryParameters = [
|
||||
pattern:
|
||||
'^(IS|IS_NOT|IS_ANY_OF|IS_NOT_ANY_OF):(.*?)(,([a-zA-Z0-9_]+))*$',
|
||||
},
|
||||
description: 'The state of the feature active/stale',
|
||||
description:
|
||||
'The state of the feature active/stale. The state can be specified with an operator. The supported operators are IS, IS_NOT, IS_ANY_OF, IS_NOT_ANY_OF.',
|
||||
in: 'query',
|
||||
},
|
||||
{
|
||||
@ -64,7 +66,8 @@ export const featureSearchQueryParameters = [
|
||||
'^(INCLUDE|DO_NOT_INCLUDE|INCLUDE_ALL_OF|INCLUDE_ANY_OF|EXCLUDE_IF_ANY_OF|EXCLUDE_ALL):(.*?)(,([a-zA-Z0-9_]+))*$',
|
||||
example: 'INCLUDE:pro-users',
|
||||
},
|
||||
description: 'The list of segments with operators to filter by.',
|
||||
description:
|
||||
'The list of segments with operators to filter by. The segment valid operators are INCLUDE, DO_NOT_INCLUDE, INCLUDE_ALL_OF, INCLUDE_ANY_OF, EXCLUDE_IF_ANY_OF, EXCLUDE_ALL.',
|
||||
in: 'query',
|
||||
},
|
||||
{
|
||||
@ -130,6 +133,17 @@ export const featureSearchQueryParameters = [
|
||||
'The flag to indicate if the favorite features should be returned first. By default it is set to false.',
|
||||
in: 'query',
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
schema: {
|
||||
type: 'string',
|
||||
example: 'IS_ON_OR_AFTER:2023-01-28T15:21:39.975Z',
|
||||
pattern: '^(IS_BEFORE|IS_ON_OR_AFTER):(.*?)(,([a-zA-Z0-9_]+))*$',
|
||||
},
|
||||
description:
|
||||
'The date the feature was created. The date can be specified with an operator. The supported operators are IS_BEFORE, IS_ON_OR_AFTER.',
|
||||
in: 'query',
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type FeatureSearchQueryParameters = Partial<
|
||||
|
Loading…
Reference in New Issue
Block a user