mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-12 13:48:35 +02:00
feat: event log environment filter (#9979)
Add Environment Filter to Event Log
This commit is contained in:
parent
c0c7005859
commit
94c73bbc5d
@ -9,13 +9,16 @@ import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureS
|
|||||||
import { EventSchemaType, type FeatureSearchResponseSchema } from 'openapi';
|
import { EventSchemaType, type FeatureSearchResponseSchema } from 'openapi';
|
||||||
import type { ProjectSchema } from 'openapi';
|
import type { ProjectSchema } from 'openapi';
|
||||||
import { useEventCreators } from 'hooks/api/getters/useEventCreators/useEventCreators';
|
import { useEventCreators } from 'hooks/api/getters/useEventCreators/useEventCreators';
|
||||||
|
import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments';
|
||||||
|
|
||||||
export const useEventLogFilters = (
|
export const useEventLogFilters = (
|
||||||
projects: ProjectSchema[],
|
projects: ProjectSchema[],
|
||||||
features: FeatureSearchResponseSchema[],
|
features: FeatureSearchResponseSchema[],
|
||||||
) => {
|
) => {
|
||||||
|
const { environments } = useEnvironments();
|
||||||
const { eventCreators } = useEventCreators();
|
const { eventCreators } = useEventCreators();
|
||||||
const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
|
const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const projectOptions =
|
const projectOptions =
|
||||||
projects?.map((project: ProjectSchema) => ({
|
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[] = [
|
const availableFilters: IFilterItem[] = [
|
||||||
{
|
{
|
||||||
label: 'Date From',
|
label: 'Date From',
|
||||||
@ -102,6 +111,18 @@ export const useEventLogFilters = (
|
|||||||
},
|
},
|
||||||
] as IFilterItem[])
|
] as IFilterItem[])
|
||||||
: []),
|
: []),
|
||||||
|
...(environmentOptions.length > 0
|
||||||
|
? ([
|
||||||
|
{
|
||||||
|
label: 'Environment',
|
||||||
|
icon: 'cloud',
|
||||||
|
options: environmentOptions,
|
||||||
|
filterKey: 'environment',
|
||||||
|
singularOperators: ['IS'],
|
||||||
|
pluralOperators: ['IS_ANY_OF'],
|
||||||
|
},
|
||||||
|
] as IFilterItem[])
|
||||||
|
: []),
|
||||||
];
|
];
|
||||||
|
|
||||||
setAvailableFilters(availableFilters);
|
setAvailableFilters(availableFilters);
|
||||||
@ -109,6 +130,7 @@ export const useEventLogFilters = (
|
|||||||
JSON.stringify(features),
|
JSON.stringify(features),
|
||||||
JSON.stringify(projects),
|
JSON.stringify(projects),
|
||||||
JSON.stringify(eventCreators),
|
JSON.stringify(eventCreators),
|
||||||
|
JSON.stringify(environments),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return availableFilters;
|
return availableFilters;
|
||||||
|
@ -71,6 +71,7 @@ export const useEventLogSearch = (
|
|||||||
}),
|
}),
|
||||||
createdBy: FilterItemParam,
|
createdBy: FilterItemParam,
|
||||||
type: FilterItemParam,
|
type: FilterItemParam,
|
||||||
|
environment: FilterItemParam,
|
||||||
...extraParameters(logType),
|
...extraParameters(logType),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -213,7 +213,6 @@ export default class EventService {
|
|||||||
representation: 'date',
|
representation: 'date',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
queryParams.push({
|
queryParams.push({
|
||||||
field: parsed.field,
|
field: parsed.field,
|
||||||
operator: 'IS_BEFORE',
|
operator: 'IS_BEFORE',
|
||||||
@ -238,7 +237,7 @@ export default class EventService {
|
|||||||
if (parsed) queryParams.push(parsed);
|
if (parsed) queryParams.push(parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
['project', 'type'].forEach((field) => {
|
['project', 'type', 'environment'].forEach((field) => {
|
||||||
if (params[field]) {
|
if (params[field]) {
|
||||||
const parsed = parseSearchOperatorValue(field, params[field]);
|
const parsed = parseSearchOperatorValue(field, params[field]);
|
||||||
if (parsed) queryParams.push(parsed);
|
if (parsed) queryParams.push(parsed);
|
||||||
|
@ -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.',
|
'The number of feature environments to return in a page. By default it is set to 50. The maximum is 1000.',
|
||||||
in: 'query',
|
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;
|
] as const;
|
||||||
|
|
||||||
export type EventSearchQueryParameters = Partial<
|
export type EventSearchQueryParameters = Partial<
|
||||||
|
@ -16,6 +16,7 @@ export interface IEventSearchParams {
|
|||||||
to?: string;
|
to?: string;
|
||||||
createdBy?: string;
|
createdBy?: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
environment?: string;
|
||||||
offset: number;
|
offset: number;
|
||||||
limit: number;
|
limit: number;
|
||||||
}
|
}
|
||||||
|
@ -575,3 +575,82 @@ test('should show user creation events for admins', async () => {
|
|||||||
total: 2,
|
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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user