mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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