1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

fix: reducing of features will not break order anymore (#5654)

This commit is contained in:
Jaanus Sellin 2023-12-15 14:46:40 +02:00 committed by GitHub
parent 8283edfc0a
commit dafec2e672
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 41 deletions

View File

@ -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,

View File

@ -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');

View File

@ -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;