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

feat: event log environment filter (#9979)

Add Environment Filter to Event Log
This commit is contained in:
Fredrik Strand Oseberg 2025-05-13 13:45:22 +02:00 committed by GitHub
parent c0c7005859
commit 94c73bbc5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 115 additions and 2 deletions

View File

@ -9,13 +9,16 @@ import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureS
import { EventSchemaType, type FeatureSearchResponseSchema } from 'openapi';
import type { ProjectSchema } from 'openapi';
import { useEventCreators } from 'hooks/api/getters/useEventCreators/useEventCreators';
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
export const useEventLogFilters = (
projects: ProjectSchema[],
features: FeatureSearchResponseSchema[],
) => {
const { environments } = useEnvironments();
const { eventCreators } = useEventCreators();
const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
useEffect(() => {
const projectOptions =
projects?.map((project: ProjectSchema) => ({
@ -41,6 +44,12 @@ export const useEventLogFilters = (
}),
);
const environmentOptions =
environments?.map((env) => ({
label: env.name,
value: env.name,
})) ?? [];
const availableFilters: IFilterItem[] = [
{
label: 'Date From',
@ -102,6 +111,18 @@ export const useEventLogFilters = (
},
] as IFilterItem[])
: []),
...(environmentOptions.length > 0
? ([
{
label: 'Environment',
icon: 'cloud',
options: environmentOptions,
filterKey: 'environment',
singularOperators: ['IS'],
pluralOperators: ['IS_ANY_OF'],
},
] as IFilterItem[])
: []),
];
setAvailableFilters(availableFilters);
@ -109,6 +130,7 @@ export const useEventLogFilters = (
JSON.stringify(features),
JSON.stringify(projects),
JSON.stringify(eventCreators),
JSON.stringify(environments),
]);
return availableFilters;

View File

@ -71,6 +71,7 @@ export const useEventLogSearch = (
}),
createdBy: FilterItemParam,
type: FilterItemParam,
environment: FilterItemParam,
...extraParameters(logType),
};

View File

@ -213,7 +213,6 @@ export default class EventService {
representation: 'date',
}),
);
queryParams.push({
field: parsed.field,
operator: 'IS_BEFORE',
@ -238,7 +237,7 @@ export default class EventService {
if (parsed) queryParams.push(parsed);
}
['project', 'type'].forEach((field) => {
['project', 'type', 'environment'].forEach((field) => {
if (params[field]) {
const parsed = parseSearchOperatorValue(field, params[field]);
if (parsed) queryParams.push(parsed);

View File

@ -100,6 +100,17 @@ export const eventSearchQueryParameters = [
'The number of feature environments to return in a page. By default it is set to 50. The maximum is 1000.',
in: 'query',
},
{
name: 'environment',
schema: {
type: 'string',
example: 'IS:production',
pattern: '^(IS|IS_ANY_OF):(.*?)(,([a-zA-Z0-9_]+))*$',
},
description:
'Filter by environment name using supported operators: IS, IS_ANY_OF.',
in: 'query',
},
] as const;
export type EventSearchQueryParameters = Partial<

View File

@ -16,6 +16,7 @@ export interface IEventSearchParams {
to?: string;
createdBy?: string;
type?: string;
environment?: string;
offset: number;
limit: number;
}

View File

@ -575,3 +575,82 @@ test('should show user creation events for admins', async () => {
total: 2,
});
});
test('should filter events by environment', async () => {
await eventService.storeEvent({
type: FEATURE_CREATED,
project: 'default',
environment: 'production',
createdBy: 'test-user',
createdByUserId: TEST_USER_ID,
ip: '127.0.0.1',
});
await eventService.storeEvent({
type: FEATURE_CREATED,
project: 'default',
environment: 'staging',
createdBy: 'test-user',
createdByUserId: TEST_USER_ID,
ip: '127.0.0.1',
});
const { body } = await searchEvents({ environment: 'IS:production' });
expect(body).toMatchObject({
events: [
{
type: 'feature-created',
environment: 'production',
},
],
total: 1,
});
});
test('should filter events by environment using IS_ANY_OF', async () => {
await eventService.storeEvent({
type: FEATURE_CREATED,
project: 'default',
environment: 'production',
createdBy: 'test-user',
createdByUserId: TEST_USER_ID,
ip: '127.0.0.1',
});
await eventService.storeEvent({
type: FEATURE_CREATED,
project: 'default',
environment: 'staging',
createdBy: 'test-user',
createdByUserId: TEST_USER_ID,
ip: '127.0.0.1',
});
await eventService.storeEvent({
type: FEATURE_CREATED,
project: 'default',
environment: 'development',
createdBy: 'test-user',
createdByUserId: TEST_USER_ID,
ip: '127.0.0.1',
});
const { body } = await searchEvents({
environment: 'IS_ANY_OF:production,staging',
});
expect(body).toMatchObject({
events: [
{
type: 'feature-created',
environment: 'staging',
},
{
type: 'feature-created',
environment: 'production',
},
],
total: 2,
});
});