1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-23 01:16:27 +02: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:
Fredrik Strand Oseberg 2023-10-18 16:34:42 +02:00 committed by GitHub
parent 1f8d12bcdc
commit cd864ed09e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 90 additions and 24 deletions

View File

@ -123,6 +123,8 @@
"knex": "^2.4.2", "knex": "^2.4.2",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.groupby": "^4.6.0", "lodash.groupby": "^4.6.0",
"lodash.isequal": "^4.5.0",
"lodash.sortby": "^4.7.0",
"log4js": "^6.0.0", "log4js": "^6.0.0",
"make-fetch-happen": "^11.0.0", "make-fetch-happen": "^11.0.0",
"memoizee": "^0.4.15", "memoizee": "^0.4.15",

View File

@ -49,7 +49,12 @@ export const createStores = (
return { return {
eventStore, eventStore,
featureToggleStore: new FeatureToggleStore(db, eventBus, getLogger), featureToggleStore: new FeatureToggleStore(
db,
eventBus,
getLogger,
config.flagResolver,
),
featureTypeStore: new FeatureTypeStore(db, getLogger), featureTypeStore: new FeatureTypeStore(db, getLogger),
strategyStore: new StrategyStore(db, getLogger), strategyStore: new StrategyStore(db, getLogger),
clientApplicationsStore: new ClientApplicationsStore( clientApplicationsStore: new ClientApplicationsStore(

View File

@ -157,6 +157,7 @@ export const deferredExportImportTogglesService = (
db, db,
eventBus, eventBus,
getLogger, getLogger,
flagResolver,
); );
const tagStore = new TagStore(db, eventBus, getLogger); const tagStore = new TagStore(db, eventBus, getLogger);
const tagTypeStore = new TagTypeStore(db, eventBus, getLogger); const tagTypeStore = new TagTypeStore(db, eventBus, getLogger);

View File

@ -5,12 +5,19 @@ import {
FeatureToggle, FeatureToggle,
IFeatureToggleQuery, IFeatureToggleQuery,
ITag, ITag,
IFlagResolver,
} from '../../../types'; } from '../../../types';
import { mapValues, ensureStringValue } from '../../../util'; import { mapValues, ensureStringValue } from '../../../util';
import { FeatureConfigurationClient } from '../types/feature-toggle-strategies-store-type'; import { FeatureConfigurationClient } from '../types/feature-toggle-strategies-store-type';
export class FeatureToggleRowConverter { export class FeatureToggleRowConverter {
private flagResolver: IFlagResolver;
constructor(flagResolver: IFlagResolver) {
this.flagResolver = flagResolver;
}
isUnseenStrategyRow = ( isUnseenStrategyRow = (
feature: PartialDeep<IFeatureToggleClient>, feature: PartialDeep<IFeatureToggleClient>,
row: Record<string, any>, row: Record<string, any>,
@ -63,7 +70,9 @@ export class FeatureToggleRowConverter {
}; };
rowToStrategy = (row: Record<string, any>): IStrategyConfig => { rowToStrategy = (row: Record<string, any>): IStrategyConfig => {
const strategy: IStrategyConfig = { let strategy: IStrategyConfig;
if (this.flagResolver.isEnabled('playgroundImprovements')) {
strategy = {
id: row.strategy_id, id: row.strategy_id,
name: row.strategy_name, name: row.strategy_name,
title: row.strategy_title, title: row.strategy_title,
@ -72,6 +81,16 @@ export class FeatureToggleRowConverter {
sortOrder: row.sort_order, sortOrder: row.sort_order,
disabled: row.strategy_disabled, 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 || []; strategy.variants = row.strategy_variants || [];
return strategy; return strategy;
}; };

View File

@ -64,7 +64,12 @@ export const createFeatureToggleService = (
getLogger, getLogger,
flagResolver, flagResolver,
); );
const featureToggleStore = new FeatureToggleStore(db, eventBus, getLogger); const featureToggleStore = new FeatureToggleStore(
db,
eventBus,
getLogger,
flagResolver,
);
const featureToggleClientStore = new FeatureToggleClientStore( const featureToggleClientStore = new FeatureToggleClientStore(
db, db,
eventBus, eventBus,

View File

@ -1,3 +1,5 @@
import sortBy from 'lodash.sortby';
interface Difference { interface Difference {
index: (string | number)[]; index: (string | number)[];
reason: string; reason: string;
@ -18,8 +20,11 @@ export function deepDiff(arr1: any[], arr2: any[]): Difference[] | null {
valueB: b, valueB: b,
}); });
} else { } else {
for (let i = 0; i < a.length; i++) { const sortedA = sortBy(a, 'name');
compare(a[i], b[i], parentIndex.concat(i)); const sortedB = sortBy(b, 'name');
for (let i = 0; i < sortedA.length; i++) {
compare(sortedA[i], sortedB[i], parentIndex.concat(i));
} }
} }
} else if ( } else if (
@ -31,7 +36,10 @@ export function deepDiff(arr1: any[], arr2: any[]): Difference[] | null {
const keysA = Object.keys(a); const keysA = Object.keys(a);
const keysB = Object.keys(b); const keysB = Object.keys(b);
if (!arraysEqual(keysA, keysB)) { if (
keysA.length !== keysB.length ||
!keysA.every((key) => keysB.includes(key))
) {
diff.push({ diff.push({
index: parentIndex, index: parentIndex,
reason: 'Different keys', 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, []); compare(arr1, arr2, []);
return diff.length > 0 ? diff : null; return diff.length > 0 ? diff : null;

View File

@ -64,10 +64,19 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
private featureToggleRowConverter: FeatureToggleRowConverter; 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.db = db;
this.logger = getLogger('feature-toggle-store.ts'); this.logger = getLogger('feature-toggle-store.ts');
this.featureToggleRowConverter = new FeatureToggleRowConverter(); this.featureToggleRowConverter = new FeatureToggleRowConverter(
flagResolver,
);
this.timer = (action) => this.timer = (action) =>
metricsHelper.wrapTimer(eventBus, DB_TIME, { metricsHelper.wrapTimer(eventBus, DB_TIME, {
store: 'feature-toggle', store: 'feature-toggle',

View File

@ -1,6 +1,24 @@
import { deepDiff } from '../deep-diff'; // Import the deepDiff function import { deepDiff } from '../deep-diff'; // Import the deepDiff function
describe('deepDiff', () => { 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', () => { it('should return null for equal arrays', () => {
const arr1 = [1, 2, 3]; const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3]; const arr2 = [1, 2, 3];

View File

@ -57,7 +57,12 @@ export const createProjectService = (
flagResolver, flagResolver,
); );
const groupStore = new GroupStore(db); 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 featureTypeStore = new FeatureTypeStore(db, getLogger);
const accountStore = new AccountStore(db, getLogger); const accountStore = new AccountStore(db, getLogger);
const environmentStore = new EnvironmentStore(db, eventBus, getLogger); const environmentStore = new EnvironmentStore(db, eventBus, getLogger);

View File

@ -19,6 +19,7 @@ export const createLastSeenService = (
db, db,
config.eventBus, config.eventBus,
config.getLogger, config.getLogger,
config.flagResolver,
); );
return new LastSeenService({ lastSeenStore, featureToggleStore }, config); return new LastSeenService({ lastSeenStore, featureToggleStore }, config);