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

chore: code cleanup: event log filters (#7870)

Adds tests for event log filters (to ensure we show the right filters)
and refactors the implementation of eventlogfilters.

Primary goal of refactoring:
- Make it so that all filters are created in one single list (instead of
injected from different variables)
- Avoid making a requests for features (and to a lesser extent:
projects) if you can't use them for filters
- Improve code structure
This commit is contained in:
Thomas Heartman 2024-08-14 15:27:22 +02:00 committed by GitHub
parent dad30a08f0
commit f59b77571c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 169 additions and 70 deletions

View File

@ -0,0 +1,68 @@
import { renderHook } from '@testing-library/react';
import { useEventLogFilters } from './EventLogFilters';
const allFilterKeys = ['from', 'to', 'createdBy', 'type', 'project', 'feature'];
allFilterKeys.sort();
test('When you have no projects or flags, you should not get a project or flag filters', () => {
const { result } = renderHook(() =>
useEventLogFilters(
() => ({ projects: [] }),
() => ({ features: [] }),
),
);
const filterKeys = result.current.map((filter) => filter.filterKey);
filterKeys.sort();
expect(filterKeys).not.toContain('project');
expect(filterKeys).not.toContain('feature');
});
test('When you have no projects, you should not get a project filter', () => {
const { result } = renderHook(() =>
useEventLogFilters(
() => ({ projects: [] }),
// @ts-expect-error: omitting other properties we don't need
() => ({ features: [{ name: 'flag' }] }),
),
);
const filterKeys = result.current.map((filter) => filter.filterKey);
filterKeys.sort();
expect(filterKeys).not.toContain('project');
});
test('When you have only one project, you should not get a project filter', () => {
const { result } = renderHook(() =>
useEventLogFilters(
// @ts-expect-error: omitting other properties we don't need
() => ({ projects: [{ id: 'a', name: 'A' }] }),
() => ({ features: [] }),
),
);
const filterKeys = result.current.map((filter) => filter.filterKey);
filterKeys.sort();
expect(filterKeys).not.toContain('project');
});
test('When you have two one project, you should not get a project filter', () => {
const { result } = renderHook(() =>
useEventLogFilters(
() => ({
projects: [
// @ts-expect-error: omitting other properties we don't need
{ id: 'a', name: 'A' },
// @ts-expect-error: omitting other properties we don't need
{ id: 'b', name: 'B' },
],
}),
() => ({ features: [] }),
),
);
const filterKeys = result.current.map((filter) => filter.filterKey);
filterKeys.sort();
expect(filterKeys).toContain('project');
});

View File

@ -6,87 +6,82 @@ import {
} from 'component/filter/Filters/Filters';
import useProjects from 'hooks/api/getters/useProjects/useProjects';
import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch';
import { EventSchemaType } from 'openapi';
import {
EventSchemaType,
type FeatureSearchResponseSchema,
type SearchFeaturesParams,
} from 'openapi';
import type { IProjectCard } from 'interfaces/project';
import { useEventCreators } from 'hooks/api/getters/useEventCreators/useEventCreators';
const useSharedFilters = (): IFilterItem[] => {
export const useEventLogFilters = (
projectsHook: () => { projects: IProjectCard[] },
featuresHook: (params: SearchFeaturesParams) => {
features: FeatureSearchResponseSchema[];
},
) => {
const { projects } = projectsHook();
const { features } = featuresHook({});
const { eventCreators } = useEventCreators();
return [
{
label: 'Date From',
icon: 'today',
options: [],
filterKey: 'from',
dateOperators: ['IS'],
},
{
label: 'Date To',
icon: 'today',
options: [],
filterKey: 'to',
dateOperators: ['IS'],
},
{
label: 'Created by',
icon: 'person',
options: eventCreators.map((creator) => ({
label: creator.name,
value: creator.id.toString(),
})),
filterKey: 'createdBy',
singularOperators: ['IS'],
pluralOperators: ['IS_ANY_OF'],
},
{
label: 'Event type',
icon: 'announcement',
options: Object.entries(EventSchemaType).map(([key, value]) => ({
label: key,
value: value,
})),
filterKey: 'type',
singularOperators: ['IS'],
pluralOperators: ['IS_ANY_OF'],
},
];
};
type EventLogFiltersProps = {
logType: 'flag' | 'project' | 'global';
className?: string;
state: FilterItemParamHolder;
onChange: (value: FilterItemParamHolder) => void;
};
export const EventLogFilters: FC<EventLogFiltersProps> = ({
logType,
className,
state,
onChange,
}) => {
const { projects } = useProjects();
const { features } = useFeatureSearch({});
const sharedFilters = useSharedFilters();
const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
useEffect(() => {
const projectOptions = (projects || []).map(
(project: IProjectCard) => ({
const projectOptions =
projects?.map((project: IProjectCard) => ({
label: project.name,
value: project.id,
})) ?? [];
const flagOptions =
features?.map((flag) => ({
label: flag.name,
value: flag.name,
})) ?? [];
const eventCreatorOptions = eventCreators.map((creator) => ({
label: creator.name,
value: creator.id.toString(),
}));
const eventTypeOptions = Object.entries(EventSchemaType).map(
([key, value]) => ({
label: key,
value: value,
}),
);
const hasMultipleProjects = projectOptions.length > 1;
const flagOptions = (features || []).map((flag) => ({
label: flag.name,
value: flag.name,
}));
const availableFilters: IFilterItem[] = [
...sharedFilters,
...(hasMultipleProjects && logType === 'global'
{
label: 'Date From',
icon: 'today',
options: [],
filterKey: 'from',
dateOperators: ['IS'],
},
{
label: 'Date To',
icon: 'today',
options: [],
filterKey: 'to',
dateOperators: ['IS'],
},
{
label: 'Created by',
icon: 'person',
options: eventCreatorOptions,
filterKey: 'createdBy',
singularOperators: ['IS'],
pluralOperators: ['IS_ANY_OF'],
},
{
label: 'Event type',
icon: 'announcement',
options: eventTypeOptions,
filterKey: 'type',
singularOperators: ['IS'],
pluralOperators: ['IS_ANY_OF'],
},
...(projectOptions.length > 1
? ([
{
label: 'Project',
@ -98,7 +93,7 @@ export const EventLogFilters: FC<EventLogFiltersProps> = ({
},
] as IFilterItem[])
: []),
...(logType !== 'flag'
...(flagOptions.length > 0
? ([
{
label: 'Feature Flag',
@ -116,9 +111,45 @@ export const EventLogFilters: FC<EventLogFiltersProps> = ({
}, [
JSON.stringify(features),
JSON.stringify(projects),
JSON.stringify(sharedFilters),
JSON.stringify(eventCreators),
]);
return availableFilters;
};
type LogType = 'flag' | 'project' | 'global';
const useEventLogFiltersFromLogType = (logType: LogType) => {
switch (logType) {
case 'flag':
return useEventLogFilters(
() => ({ projects: [] }),
() => ({ features: [] }),
);
case 'project':
return useEventLogFilters(
() => ({ projects: [] }),
useFeatureSearch,
);
case 'global':
return useEventLogFilters(useProjects, useFeatureSearch);
}
};
type EventLogFiltersProps = {
logType: LogType;
className?: string;
state: FilterItemParamHolder;
onChange: (value: FilterItemParamHolder) => void;
};
export const EventLogFilters: FC<EventLogFiltersProps> = ({
logType,
className,
state,
onChange,
}) => {
const availableFilters = useEventLogFiltersFromLogType(logType);
return (
<Filters
className={className}