mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: reducing of features will not break order anymore (#5654)
This commit is contained in:
		
							parent
							
								
									8283edfc0a
								
							
						
					
					
						commit
						dafec2e672
					
				| @ -19,8 +19,8 @@ import { | |||||||
| } from '../feature-toggle/types/feature-toggle-strategies-store-type'; | } from '../feature-toggle/types/feature-toggle-strategies-store-type'; | ||||||
| import FeatureStrategiesStore from '../feature-toggle/feature-toggle-strategies-store'; | import FeatureStrategiesStore from '../feature-toggle/feature-toggle-strategies-store'; | ||||||
| 
 | 
 | ||||||
| const sortEnvironments = (overview: IFeatureOverview) => { | const sortEnvironments = (overview: IFeatureOverview[]) => { | ||||||
|     return Object.values(overview).map((data: IFeatureOverview) => ({ |     return overview.map((data: IFeatureOverview) => ({ | ||||||
|         ...data, |         ...data, | ||||||
|         environments: data.environments |         environments: data.environments | ||||||
|             .filter((f) => f.name) |             .filter((f) => f.name) | ||||||
| @ -296,34 +296,16 @@ class FeatureSearchStore implements IFeatureSearchStore { | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     getAggregatedSearchData(rows): IFeatureOverview { |     getAggregatedSearchData(rows): IFeatureOverview[] { | ||||||
|         return rows.reduce((acc, row) => { |         const entriesMap: Map<string, IFeatureOverview> = new Map(); | ||||||
|             if (acc[row.feature_name] !== undefined) { |         const orderedEntries: IFeatureOverview[] = []; | ||||||
|                 const environmentExists = acc[ |  | ||||||
|                     row.feature_name |  | ||||||
|                 ].environments.some( |  | ||||||
|                     (existingEnvironment) => |  | ||||||
|                         existingEnvironment.name === row.environment, |  | ||||||
|                 ); |  | ||||||
|                 if (!environmentExists) { |  | ||||||
|                     acc[row.feature_name].environments.push( |  | ||||||
|                         FeatureSearchStore.getEnvironment(row), |  | ||||||
|                     ); |  | ||||||
|                 } |  | ||||||
| 
 | 
 | ||||||
|                 const segmentExists = acc[row.feature_name].segments.includes( |         rows.forEach((row) => { | ||||||
|                     row.segment_name, |             let entry = entriesMap.get(row.feature_name); | ||||||
|                 ); |  | ||||||
| 
 | 
 | ||||||
|                 if (row.segment_name && !segmentExists) { |             if (!entry) { | ||||||
|                     acc[row.feature_name].segments.push(row.segment_name); |                 // Create a new entry
 | ||||||
|                 } |                 entry = { | ||||||
| 
 |  | ||||||
|                 if (this.isNewTag(acc[row.feature_name], row)) { |  | ||||||
|                     this.addTag(acc[row.feature_name], row); |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 acc[row.feature_name] = { |  | ||||||
|                     type: row.type, |                     type: row.type, | ||||||
|                     description: row.description, |                     description: row.description, | ||||||
|                     project: row.project, |                     project: row.project, | ||||||
| @ -333,24 +315,41 @@ class FeatureSearchStore implements IFeatureSearchStore { | |||||||
|                     stale: row.stale, |                     stale: row.stale, | ||||||
|                     impressionData: row.impression_data, |                     impressionData: row.impression_data, | ||||||
|                     lastSeenAt: row.last_seen_at, |                     lastSeenAt: row.last_seen_at, | ||||||
|                     environments: [FeatureSearchStore.getEnvironment(row)], |                     environments: [], | ||||||
|                     segments: row.segment_name ? [row.segment_name] : [], |                     segments: row.segment_name ? [row.segment_name] : [], | ||||||
|                 }; |                 }; | ||||||
|  |                 entriesMap.set(row.feature_name, entry); | ||||||
|  |                 orderedEntries.push(entry); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|                 if (this.isNewTag(acc[row.feature_name], row)) { |             // Add environment if not already present
 | ||||||
|                     this.addTag(acc[row.feature_name], row); |             if (!entry.environments.some((e) => e.name === row.environment)) { | ||||||
|                 } |                 entry.environments.push(FeatureSearchStore.getEnvironment(row)); | ||||||
|             } |             } | ||||||
|             const featureRow = acc[row.feature_name]; | 
 | ||||||
|  |             // Add segment if not already present
 | ||||||
|             if ( |             if ( | ||||||
|                 featureRow.lastSeenAt === undefined || |                 row.segment_name && | ||||||
|                 new Date(row.env_last_seen_at) > |                 !entry.segments.includes(row.segment_name) | ||||||
|                     new Date(featureRow.last_seen_at) |  | ||||||
|             ) { |             ) { | ||||||
|                 featureRow.lastSeenAt = row.env_last_seen_at; |                 entry.segments.push(row.segment_name); | ||||||
|             } |             } | ||||||
|             return acc; | 
 | ||||||
|         }, {}); |             // Add tag if new
 | ||||||
|  |             if (this.isNewTag(entry, row)) { | ||||||
|  |                 this.addTag(entry, row); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // Update lastSeenAt if more recent
 | ||||||
|  |             if ( | ||||||
|  |                 !entry.lastSeenAt || | ||||||
|  |                 new Date(row.env_last_seen_at) > new Date(entry.lastSeenAt) | ||||||
|  |             ) { | ||||||
|  |                 entry.lastSeenAt = row.env_last_seen_at; | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         return orderedEntries; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private addTag( |     private addTag( | ||||||
| @ -369,13 +368,16 @@ class FeatureSearchStore implements IFeatureSearchStore { | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private isTagRow(row: Record<string, any>): boolean { | ||||||
|  |         return row.tag_type && row.tag_value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private isNewTag( |     private isNewTag( | ||||||
|         featureToggle: Record<string, any>, |         featureToggle: Record<string, any>, | ||||||
|         row: Record<string, any>, |         row: Record<string, any>, | ||||||
|     ): boolean { |     ): boolean { | ||||||
|         return ( |         return ( | ||||||
|             row.tag_type && |             this.isTagRow(row) && | ||||||
|             row.tag_value && |  | ||||||
|             !featureToggle.tags?.some( |             !featureToggle.tags?.some( | ||||||
|                 (tag) => |                 (tag) => | ||||||
|                     tag.type === row.tag_type && tag.value === row.tag_value, |                     tag.type === row.tag_type && tag.value === row.tag_value, | ||||||
|  | |||||||
| @ -477,6 +477,31 @@ test('should sort features', async () => { | |||||||
|         total: 3, |         total: 3, | ||||||
|     }); |     }); | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | test('should sort features when feature names are numbers', async () => { | ||||||
|  |     await app.createFeature('my_feature_a'); | ||||||
|  |     await app.createFeature('my_feature_c'); | ||||||
|  |     await app.createFeature('my_feature_b'); | ||||||
|  |     await app.createFeature('1234'); | ||||||
|  |     await app.favoriteFeature('my_feature_b'); | ||||||
|  | 
 | ||||||
|  |     const { body: favoriteSortByName } = await sortFeatures({ | ||||||
|  |         sortBy: 'name', | ||||||
|  |         sortOrder: 'asc', | ||||||
|  |         favoritesFirst: 'true', | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     expect(favoriteSortByName).toMatchObject({ | ||||||
|  |         features: [ | ||||||
|  |             { name: 'my_feature_b' }, | ||||||
|  |             { name: '1234' }, | ||||||
|  |             { name: 'my_feature_a' }, | ||||||
|  |             { name: 'my_feature_c' }, | ||||||
|  |         ], | ||||||
|  |         total: 4, | ||||||
|  |     }); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| test('should paginate correctly when using tags', async () => { | test('should paginate correctly when using tags', async () => { | ||||||
|     await app.createFeature('my_feature_a'); |     await app.createFeature('my_feature_a'); | ||||||
|     await app.createFeature('my_feature_b'); |     await app.createFeature('my_feature_b'); | ||||||
|  | |||||||
| @ -205,6 +205,11 @@ export interface IEnvironmentOverview extends IEnvironmentBase { | |||||||
| 
 | 
 | ||||||
| export interface IFeatureOverview { | export interface IFeatureOverview { | ||||||
|     name: string; |     name: string; | ||||||
|  |     description: string; | ||||||
|  |     project: string; | ||||||
|  |     favorite: boolean; | ||||||
|  |     impressionData: boolean; | ||||||
|  |     segments: string[]; | ||||||
|     type: string; |     type: string; | ||||||
|     stale: boolean; |     stale: boolean; | ||||||
|     createdAt: Date; |     createdAt: Date; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user