mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	fix: add sort to deep diff (#5084)
Sort array items before running compare. Feature flag certain properties of strategy that were previously not present in the /api/admin/features endpoint.
This commit is contained in:
		
							parent
							
								
									1f8d12bcdc
								
							
						
					
					
						commit
						cd864ed09e
					
				| @ -123,6 +123,8 @@ | ||||
|     "knex": "^2.4.2", | ||||
|     "lodash.get": "^4.4.2", | ||||
|     "lodash.groupby": "^4.6.0", | ||||
|     "lodash.isequal": "^4.5.0", | ||||
|     "lodash.sortby": "^4.7.0", | ||||
|     "log4js": "^6.0.0", | ||||
|     "make-fetch-happen": "^11.0.0", | ||||
|     "memoizee": "^0.4.15", | ||||
|  | ||||
| @ -49,7 +49,12 @@ export const createStores = ( | ||||
| 
 | ||||
|     return { | ||||
|         eventStore, | ||||
|         featureToggleStore: new FeatureToggleStore(db, eventBus, getLogger), | ||||
|         featureToggleStore: new FeatureToggleStore( | ||||
|             db, | ||||
|             eventBus, | ||||
|             getLogger, | ||||
|             config.flagResolver, | ||||
|         ), | ||||
|         featureTypeStore: new FeatureTypeStore(db, getLogger), | ||||
|         strategyStore: new StrategyStore(db, getLogger), | ||||
|         clientApplicationsStore: new ClientApplicationsStore( | ||||
|  | ||||
| @ -157,6 +157,7 @@ export const deferredExportImportTogglesService = ( | ||||
|             db, | ||||
|             eventBus, | ||||
|             getLogger, | ||||
|             flagResolver, | ||||
|         ); | ||||
|         const tagStore = new TagStore(db, eventBus, getLogger); | ||||
|         const tagTypeStore = new TagTypeStore(db, eventBus, getLogger); | ||||
|  | ||||
| @ -5,12 +5,19 @@ import { | ||||
|     FeatureToggle, | ||||
|     IFeatureToggleQuery, | ||||
|     ITag, | ||||
|     IFlagResolver, | ||||
| } from '../../../types'; | ||||
| 
 | ||||
| import { mapValues, ensureStringValue } from '../../../util'; | ||||
| import { FeatureConfigurationClient } from '../types/feature-toggle-strategies-store-type'; | ||||
| 
 | ||||
| export class FeatureToggleRowConverter { | ||||
|     private flagResolver: IFlagResolver; | ||||
| 
 | ||||
|     constructor(flagResolver: IFlagResolver) { | ||||
|         this.flagResolver = flagResolver; | ||||
|     } | ||||
| 
 | ||||
|     isUnseenStrategyRow = ( | ||||
|         feature: PartialDeep<IFeatureToggleClient>, | ||||
|         row: Record<string, any>, | ||||
| @ -63,15 +70,27 @@ export class FeatureToggleRowConverter { | ||||
|     }; | ||||
| 
 | ||||
|     rowToStrategy = (row: Record<string, any>): IStrategyConfig => { | ||||
|         const strategy: IStrategyConfig = { | ||||
|             id: row.strategy_id, | ||||
|             name: row.strategy_name, | ||||
|             title: row.strategy_title, | ||||
|             constraints: row.constraints || [], | ||||
|             parameters: mapValues(row.parameters || {}, ensureStringValue), | ||||
|             sortOrder: row.sort_order, | ||||
|             disabled: row.strategy_disabled, | ||||
|         }; | ||||
|         let strategy: IStrategyConfig; | ||||
|         if (this.flagResolver.isEnabled('playgroundImprovements')) { | ||||
|             strategy = { | ||||
|                 id: row.strategy_id, | ||||
|                 name: row.strategy_name, | ||||
|                 title: row.strategy_title, | ||||
|                 constraints: row.constraints || [], | ||||
|                 parameters: mapValues(row.parameters || {}, ensureStringValue), | ||||
|                 sortOrder: row.sort_order, | ||||
|                 disabled: row.strategy_disabled, | ||||
|             }; | ||||
|         } else { | ||||
|             strategy = { | ||||
|                 id: row.strategy_id, | ||||
|                 name: row.strategy_name, | ||||
|                 constraints: row.constraints || [], | ||||
|                 parameters: mapValues(row.parameters || {}, ensureStringValue), | ||||
|                 sortOrder: row.sort_order, | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         strategy.variants = row.strategy_variants || []; | ||||
|         return strategy; | ||||
|     }; | ||||
|  | ||||
| @ -64,7 +64,12 @@ export const createFeatureToggleService = ( | ||||
|         getLogger, | ||||
|         flagResolver, | ||||
|     ); | ||||
|     const featureToggleStore = new FeatureToggleStore(db, eventBus, getLogger); | ||||
|     const featureToggleStore = new FeatureToggleStore( | ||||
|         db, | ||||
|         eventBus, | ||||
|         getLogger, | ||||
|         flagResolver, | ||||
|     ); | ||||
|     const featureToggleClientStore = new FeatureToggleClientStore( | ||||
|         db, | ||||
|         eventBus, | ||||
|  | ||||
| @ -1,3 +1,5 @@ | ||||
| import sortBy from 'lodash.sortby'; | ||||
| 
 | ||||
| interface Difference { | ||||
|     index: (string | number)[]; | ||||
|     reason: string; | ||||
| @ -18,8 +20,11 @@ export function deepDiff(arr1: any[], arr2: any[]): Difference[] | null { | ||||
|                     valueB: b, | ||||
|                 }); | ||||
|             } else { | ||||
|                 for (let i = 0; i < a.length; i++) { | ||||
|                     compare(a[i], b[i], parentIndex.concat(i)); | ||||
|                 const sortedA = sortBy(a, 'name'); | ||||
|                 const sortedB = sortBy(b, 'name'); | ||||
| 
 | ||||
|                 for (let i = 0; i < sortedA.length; i++) { | ||||
|                     compare(sortedA[i], sortedB[i], parentIndex.concat(i)); | ||||
|                 } | ||||
|             } | ||||
|         } else if ( | ||||
| @ -31,7 +36,10 @@ export function deepDiff(arr1: any[], arr2: any[]): Difference[] | null { | ||||
|             const keysA = Object.keys(a); | ||||
|             const keysB = Object.keys(b); | ||||
| 
 | ||||
|             if (!arraysEqual(keysA, keysB)) { | ||||
|             if ( | ||||
|                 keysA.length !== keysB.length || | ||||
|                 !keysA.every((key) => keysB.includes(key)) | ||||
|             ) { | ||||
|                 diff.push({ | ||||
|                     index: parentIndex, | ||||
|                     reason: 'Different keys', | ||||
| @ -53,13 +61,6 @@ export function deepDiff(arr1: any[], arr2: any[]): Difference[] | null { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     function arraysEqual(a: any[], b: any[]): boolean { | ||||
|         return ( | ||||
|             a.length === b.length && | ||||
|             a.sort().every((val, index) => val === b.sort()[index]) | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     compare(arr1, arr2, []); | ||||
| 
 | ||||
|     return diff.length > 0 ? diff : null; | ||||
|  | ||||
| @ -64,10 +64,19 @@ export default class FeatureToggleStore implements IFeatureToggleStore { | ||||
| 
 | ||||
|     private featureToggleRowConverter: FeatureToggleRowConverter; | ||||
| 
 | ||||
|     constructor(db: Db, eventBus: EventEmitter, getLogger: LogProvider) { | ||||
|     private flagResolver: IFlagResolver; | ||||
| 
 | ||||
|     constructor( | ||||
|         db: Db, | ||||
|         eventBus: EventEmitter, | ||||
|         getLogger: LogProvider, | ||||
|         flagResolver: IFlagResolver, | ||||
|     ) { | ||||
|         this.db = db; | ||||
|         this.logger = getLogger('feature-toggle-store.ts'); | ||||
|         this.featureToggleRowConverter = new FeatureToggleRowConverter(); | ||||
|         this.featureToggleRowConverter = new FeatureToggleRowConverter( | ||||
|             flagResolver, | ||||
|         ); | ||||
|         this.timer = (action) => | ||||
|             metricsHelper.wrapTimer(eventBus, DB_TIME, { | ||||
|                 store: 'feature-toggle', | ||||
|  | ||||
| @ -1,6 +1,24 @@ | ||||
| import { deepDiff } from '../deep-diff'; // Import the deepDiff function
 | ||||
| 
 | ||||
| describe('deepDiff', () => { | ||||
|     test('should sort arrays by name before comparing', () => { | ||||
|         // Define two arrays that are identical except for the order of elements
 | ||||
|         const array1 = [ | ||||
|             { name: 'b', value: 2 }, | ||||
|             { name: 'a', value: 1 }, | ||||
|         ]; | ||||
|         const array2 = [ | ||||
|             { name: 'a', value: 1 }, | ||||
|             { name: 'b', value: 2 }, | ||||
|         ]; | ||||
| 
 | ||||
|         // If the function correctly sorts before comparing, there should be no differences
 | ||||
|         const result = deepDiff(array1, array2); | ||||
| 
 | ||||
|         // Assert that there is no difference
 | ||||
|         expect(result).toBeNull(); | ||||
|     }); | ||||
| 
 | ||||
|     it('should return null for equal arrays', () => { | ||||
|         const arr1 = [1, 2, 3]; | ||||
|         const arr2 = [1, 2, 3]; | ||||
|  | ||||
| @ -57,7 +57,12 @@ export const createProjectService = ( | ||||
|         flagResolver, | ||||
|     ); | ||||
|     const groupStore = new GroupStore(db); | ||||
|     const featureToggleStore = new FeatureToggleStore(db, eventBus, getLogger); | ||||
|     const featureToggleStore = new FeatureToggleStore( | ||||
|         db, | ||||
|         eventBus, | ||||
|         getLogger, | ||||
|         flagResolver, | ||||
|     ); | ||||
|     const featureTypeStore = new FeatureTypeStore(db, getLogger); | ||||
|     const accountStore = new AccountStore(db, getLogger); | ||||
|     const environmentStore = new EnvironmentStore(db, eventBus, getLogger); | ||||
|  | ||||
| @ -19,6 +19,7 @@ export const createLastSeenService = ( | ||||
|         db, | ||||
|         config.eventBus, | ||||
|         config.getLogger, | ||||
|         config.flagResolver, | ||||
|     ); | ||||
| 
 | ||||
|     return new LastSeenService({ lastSeenStore, featureToggleStore }, config); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user