mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-07 01:16:28 +02:00
chore: event timeline tooltips
This commit is contained in:
parent
6d51213f55
commit
a413a38352
@ -1,5 +1,5 @@
|
|||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import type { EventSchemaType } from 'openapi';
|
import type { EventSchema, EventSchemaType } from 'openapi';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { startOfDay, sub } from 'date-fns';
|
import { startOfDay, sub } from 'date-fns';
|
||||||
import type { IEnvironment } from 'interfaces/environments';
|
import type { IEnvironment } from 'interfaces/environments';
|
||||||
@ -11,6 +11,11 @@ import {
|
|||||||
timeSpanOptions,
|
timeSpanOptions,
|
||||||
} from './EventTimelineHeader/EventTimelineHeader';
|
} from './EventTimelineHeader/EventTimelineHeader';
|
||||||
|
|
||||||
|
export type EnrichedEvent = EventSchema & {
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
const StyledRow = styled('div')({
|
const StyledRow = styled('div')({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
@ -91,7 +96,7 @@ export const EventTimeline = () => {
|
|||||||
const endDate = new Date();
|
const endDate = new Date();
|
||||||
const startDate = sub(endDate, timeSpan.value);
|
const startDate = sub(endDate, timeSpan.value);
|
||||||
|
|
||||||
const { events } = useEventSearch(
|
const { events: baseEvents } = useEventSearch(
|
||||||
{
|
{
|
||||||
from: `IS:${toISODateString(startOfDay(startDate))}`,
|
from: `IS:${toISODateString(startOfDay(startDate))}`,
|
||||||
to: `IS:${toISODateString(endDate)}`,
|
to: `IS:${toISODateString(endDate)}`,
|
||||||
@ -100,6 +105,8 @@ export const EventTimeline = () => {
|
|||||||
{ refreshInterval: 10 * 1000 },
|
{ refreshInterval: 10 * 1000 },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const events = baseEvents as EnrichedEvent[];
|
||||||
|
|
||||||
const filteredEvents = events.filter(
|
const filteredEvents = events.filter(
|
||||||
(event) =>
|
(event) =>
|
||||||
new Date(event.createdAt).getTime() >= startDate.getTime() &&
|
new Date(event.createdAt).getTime() >= startDate.getTime() &&
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { EventSchema, EventSchemaType } from 'openapi';
|
import type { EventSchemaType } from 'openapi';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||||
import { EventTimelineEventTooltip } from './EventTimelineEventTooltip/EventTimelineEventTooltip';
|
import { EventTimelineEventTooltip } from './EventTimelineEventTooltip/EventTimelineEventTooltip';
|
||||||
@ -8,6 +8,7 @@ import FlagOutlinedIcon from '@mui/icons-material/FlagOutlined';
|
|||||||
import ExtensionOutlinedIcon from '@mui/icons-material/ExtensionOutlined';
|
import ExtensionOutlinedIcon from '@mui/icons-material/ExtensionOutlined';
|
||||||
import SegmentsIcon from '@mui/icons-material/DonutLargeOutlined';
|
import SegmentsIcon from '@mui/icons-material/DonutLargeOutlined';
|
||||||
import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
|
import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
|
||||||
|
import type { EnrichedEvent } from '../EventTimeline';
|
||||||
|
|
||||||
type DefaultEventVariant = 'secondary';
|
type DefaultEventVariant = 'secondary';
|
||||||
type CustomEventVariant = 'success' | 'neutral';
|
type CustomEventVariant = 'success' | 'neutral';
|
||||||
@ -76,7 +77,7 @@ const customEventVariants: Partial<
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface IEventTimelineEventProps {
|
interface IEventTimelineEventProps {
|
||||||
event: EventSchema;
|
event: EnrichedEvent;
|
||||||
startDate: Date;
|
startDate: Date;
|
||||||
endDate: Date;
|
endDate: Date;
|
||||||
}
|
}
|
||||||
@ -97,6 +98,7 @@ export const EventTimelineEvent = ({
|
|||||||
<StyledEvent position={position}>
|
<StyledEvent position={position}>
|
||||||
<HtmlTooltip
|
<HtmlTooltip
|
||||||
title={<EventTimelineEventTooltip event={event} />}
|
title={<EventTimelineEventTooltip event={event} />}
|
||||||
|
maxWidth={320}
|
||||||
arrow
|
arrow
|
||||||
>
|
>
|
||||||
<StyledEventCircle variant={variant}>
|
<StyledEventCircle variant={variant}>
|
||||||
|
@ -1,9 +1,32 @@
|
|||||||
|
import { styled } from '@mui/material';
|
||||||
|
import { Markdown } from 'component/common/Markdown/Markdown';
|
||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||||
import type { EventSchema } from 'openapi';
|
|
||||||
import { formatDateYMDHMS } from 'utils/formatDate';
|
import { formatDateYMDHMS } from 'utils/formatDate';
|
||||||
|
import type { EnrichedEvent } from '../../EventTimeline';
|
||||||
|
|
||||||
|
const StyledTooltipHeader = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTooltipTitle = styled('div')(({ theme }) => ({
|
||||||
|
fontWeight: theme.fontWeight.bold,
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
flex: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDateTime = styled('div')(({ theme }) => ({
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
}));
|
||||||
|
|
||||||
interface IEventTimelineEventTooltipProps {
|
interface IEventTimelineEventTooltipProps {
|
||||||
event: EventSchema;
|
event: EnrichedEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EventTimelineEventTooltip = ({
|
export const EventTimelineEventTooltip = ({
|
||||||
@ -15,36 +38,13 @@ export const EventTimelineEventTooltip = ({
|
|||||||
locationSettings?.locale,
|
locationSettings?.locale,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (event.type === 'feature-environment-enabled') {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<small>{eventDateTime}</small>
|
|
||||||
<p>
|
|
||||||
{event.createdBy} enabled {event.featureName} for the{' '}
|
|
||||||
{event.environment} environment in project {event.project}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (event.type === 'feature-environment-disabled') {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<small>{eventDateTime}</small>
|
|
||||||
<p>
|
|
||||||
{event.createdBy} disabled {event.featureName} for the{' '}
|
|
||||||
{event.environment} environment in project {event.project}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<div>{eventDateTime}</div>
|
<StyledTooltipHeader>
|
||||||
<div>{event.createdBy}</div>
|
<StyledTooltipTitle>{event.label}</StyledTooltipTitle>
|
||||||
<div>{event.type}</div>
|
<StyledDateTime>{eventDateTime}</StyledDateTime>
|
||||||
<div>{event.featureName}</div>
|
</StyledTooltipHeader>
|
||||||
<div>{event.environment}</div>
|
<Markdown>{event.description}</Markdown>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -63,11 +63,13 @@ import {
|
|||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
interface IEventData {
|
interface IEventData {
|
||||||
|
label: string;
|
||||||
action: string;
|
action: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFormattedEventData {
|
interface IFormattedEventData {
|
||||||
|
label: string;
|
||||||
text: string;
|
text: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
}
|
}
|
||||||
@ -80,237 +82,297 @@ export enum LinkStyle {
|
|||||||
MD = 1,
|
MD = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bold = (text?: string) => (text ? `**${text}**` : '');
|
||||||
|
|
||||||
const EVENT_MAP: Record<string, IEventData> = {
|
const EVENT_MAP: Record<string, IEventData> = {
|
||||||
[ADDON_CONFIG_CREATED]: {
|
[ADDON_CONFIG_CREATED]: {
|
||||||
action: '*{{user}}* created a new *{{event.data.provider}}* integration configuration',
|
label: 'Integration configuration created',
|
||||||
|
action: `${bold('{{user}}')} created a new ${bold('{{event.data.provider}}')} integration configuration`,
|
||||||
path: '/integrations',
|
path: '/integrations',
|
||||||
},
|
},
|
||||||
[ADDON_CONFIG_DELETED]: {
|
[ADDON_CONFIG_DELETED]: {
|
||||||
action: '*{{user}}* deleted a *{{event.preData.provider}}* integration configuration',
|
label: 'Integration configuration deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted a ${bold('{{event.preData.provider}}')} integration configuration`,
|
||||||
path: '/integrations',
|
path: '/integrations',
|
||||||
},
|
},
|
||||||
[ADDON_CONFIG_UPDATED]: {
|
[ADDON_CONFIG_UPDATED]: {
|
||||||
action: '*{{user}}* updated a *{{event.preData.provider}}* integration configuration',
|
label: 'Integration configuration updated',
|
||||||
|
action: `${bold('{{user}}')} updated a ${bold('{{event.preData.provider}}')} integration configuration`,
|
||||||
path: '/integrations',
|
path: '/integrations',
|
||||||
},
|
},
|
||||||
[API_TOKEN_CREATED]: {
|
[API_TOKEN_CREATED]: {
|
||||||
action: '*{{user}}* created API token *{{event.data.username}}*',
|
label: 'API token created',
|
||||||
|
action: `${bold('{{user}}')} created API token ${bold('{{event.data.username}}')}`,
|
||||||
path: '/admin/api',
|
path: '/admin/api',
|
||||||
},
|
},
|
||||||
[API_TOKEN_DELETED]: {
|
[API_TOKEN_DELETED]: {
|
||||||
action: '*{{user}}* deleted API token *{{event.preData.username}}*',
|
label: 'API token deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted API token ${bold('{{event.preData.username}}')}`,
|
||||||
path: '/admin/api',
|
path: '/admin/api',
|
||||||
},
|
},
|
||||||
[CHANGE_ADDED]: {
|
[CHANGE_ADDED]: {
|
||||||
action: '*{{user}}* added a change to change request {{changeRequest}}',
|
label: 'Change added',
|
||||||
|
action: `${bold('{{user}}')} added a change to change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_DISCARDED]: {
|
[CHANGE_DISCARDED]: {
|
||||||
action: '*{{user}}* discarded a change in change request {{changeRequest}}',
|
label: 'Change discarded',
|
||||||
|
action: `${bold('{{user}}')} discarded a change in change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_EDITED]: {
|
[CHANGE_EDITED]: {
|
||||||
action: '*{{user}}* edited a change in change request {{changeRequest}}',
|
label: 'Change edited',
|
||||||
|
action: `${bold('{{user}}')} edited a change in change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_APPLIED]: {
|
[CHANGE_REQUEST_APPLIED]: {
|
||||||
action: '*{{user}}* applied change request {{changeRequest}}',
|
label: 'Change request applied',
|
||||||
|
action: `${bold('{{user}}')} applied change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_APPROVAL_ADDED]: {
|
[CHANGE_REQUEST_APPROVAL_ADDED]: {
|
||||||
action: '*{{user}}* added an approval to change request {{changeRequest}}',
|
label: 'Change request approval added',
|
||||||
|
action: `${bold('{{user}}')} added an approval to change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_APPROVED]: {
|
[CHANGE_REQUEST_APPROVED]: {
|
||||||
action: '*{{user}}* approved change request {{changeRequest}}',
|
label: 'Change request approved',
|
||||||
|
action: `${bold('{{user}}')} approved change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_CANCELLED]: {
|
[CHANGE_REQUEST_CANCELLED]: {
|
||||||
action: '*{{user}}* cancelled change request {{changeRequest}}',
|
label: 'Change request cancelled',
|
||||||
|
action: `${bold('{{user}}')} cancelled change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_CREATED]: {
|
[CHANGE_REQUEST_CREATED]: {
|
||||||
action: '*{{user}}* created change request {{changeRequest}}',
|
label: 'Change request created',
|
||||||
|
action: `${bold('{{user}}')} created change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_DISCARDED]: {
|
[CHANGE_REQUEST_DISCARDED]: {
|
||||||
action: '*{{user}}* discarded change request {{changeRequest}}',
|
label: 'Change request discarded',
|
||||||
|
action: `${bold('{{user}}')} discarded change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_REJECTED]: {
|
[CHANGE_REQUEST_REJECTED]: {
|
||||||
action: '*{{user}}* rejected change request {{changeRequest}}',
|
label: 'Change request rejected',
|
||||||
|
action: `${bold('{{user}}')} rejected change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_SENT_TO_REVIEW]: {
|
[CHANGE_REQUEST_SENT_TO_REVIEW]: {
|
||||||
action: '*{{user}}* sent to review change request {{changeRequest}}',
|
label: 'Change request sent to review',
|
||||||
|
action: `${bold('{{user}}')} sent to review change request {{changeRequest}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_SCHEDULED]: {
|
[CHANGE_REQUEST_SCHEDULED]: {
|
||||||
action: '*{{user}}* scheduled change request {{changeRequest}} to be applied at {{event.data.scheduledDate}} in project *{{event.project}}*',
|
label: 'Change request scheduled',
|
||||||
|
action: `${bold('{{user}}')} scheduled change request {{changeRequest}} to be applied at {{event.data.scheduledDate}} in project ${bold('{{event.project}}')}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_SCHEDULED_APPLICATION_SUCCESS]: {
|
[CHANGE_REQUEST_SCHEDULED_APPLICATION_SUCCESS]: {
|
||||||
action: '*Successfully* applied the scheduled change request {{changeRequest}} by *{{user}}* in project *{{event.project}}*.',
|
label: 'Scheduled change request applied successfully',
|
||||||
|
action: `${bold('Successfully')} applied the scheduled change request {{changeRequest}} by ${bold('{{user}}')} in project ${bold('{{event.project}}')}.`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_SCHEDULED_APPLICATION_FAILURE]: {
|
[CHANGE_REQUEST_SCHEDULED_APPLICATION_FAILURE]: {
|
||||||
action: '*Failed* to apply the scheduled change request {{changeRequest}} by *{{user}}* in project *{{event.project}}*.',
|
label: 'Scheduled change request failed',
|
||||||
|
action: `${bold('Failed')} to apply the scheduled change request {{changeRequest}} by ${bold('{{user}}')} in project ${bold('{{event.project}}')}.`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CHANGE_REQUEST_SCHEDULE_SUSPENDED]: {
|
[CHANGE_REQUEST_SCHEDULE_SUSPENDED]: {
|
||||||
action: 'Change request {{changeRequest}} was suspended for the following reason: {{event.data.reason}}',
|
label: 'Change request suspended',
|
||||||
|
action: `Change request {{changeRequest}} was suspended for the following reason: {{event.data.reason}}`,
|
||||||
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
path: '/projects/{{event.project}}/change-requests/{{event.data.changeRequestId}}',
|
||||||
},
|
},
|
||||||
[CONTEXT_FIELD_CREATED]: {
|
[CONTEXT_FIELD_CREATED]: {
|
||||||
action: '*{{user}}* created context field *{{event.data.name}}*',
|
label: 'Context field created',
|
||||||
|
action: `${bold('{{user}}')} created context field ${bold('{{event.data.name}}')}`,
|
||||||
path: '/context',
|
path: '/context',
|
||||||
},
|
},
|
||||||
[CONTEXT_FIELD_DELETED]: {
|
[CONTEXT_FIELD_DELETED]: {
|
||||||
action: '*{{user}}* deleted context field *{{event.preData.name}}*',
|
label: 'Context field deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted context field ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/context',
|
path: '/context',
|
||||||
},
|
},
|
||||||
[CONTEXT_FIELD_UPDATED]: {
|
[CONTEXT_FIELD_UPDATED]: {
|
||||||
action: '*{{user}}* updated context field *{{event.preData.name}}*',
|
label: 'Context field updated',
|
||||||
|
action: `${bold('{{user}}')} updated context field ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/context',
|
path: '/context',
|
||||||
},
|
},
|
||||||
[FEATURE_ARCHIVED]: {
|
[FEATURE_ARCHIVED]: {
|
||||||
action: '*{{user}}* archived *{{event.featureName}}* in project *{{project}}*',
|
label: 'Flag archived',
|
||||||
|
action: `${bold('{{user}}')} archived ${bold('{{event.featureName}}')} in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/archive',
|
path: '/projects/{{event.project}}/archive',
|
||||||
},
|
},
|
||||||
[FEATURE_CREATED]: {
|
[FEATURE_CREATED]: {
|
||||||
action: '*{{user}}* created *{{feature}}* in project *{{project}}*',
|
label: 'Flag created',
|
||||||
|
action: `${bold('{{user}}')} created ${bold('{{feature}}')} in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_DELETED]: {
|
[FEATURE_DELETED]: {
|
||||||
action: '*{{user}}* deleted *{{event.featureName}}* in project *{{project}}*',
|
label: 'Flag deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted ${bold('{{event.featureName}}')} in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}',
|
path: '/projects/{{event.project}}',
|
||||||
},
|
},
|
||||||
[FEATURE_ENVIRONMENT_DISABLED]: {
|
[FEATURE_ENVIRONMENT_DISABLED]: {
|
||||||
action: '*{{user}}* disabled *{{feature}}* for the *{{event.environment}}* environment in project *{{project}}*',
|
label: 'Flag disabled',
|
||||||
|
action: `${bold('{{user}}')} disabled ${bold('{{feature}}')} for the ${bold('{{event.environment}}')} environment in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_ENVIRONMENT_ENABLED]: {
|
[FEATURE_ENVIRONMENT_ENABLED]: {
|
||||||
action: '*{{user}}* enabled *{{feature}}* for the *{{event.environment}}* environment in project *{{project}}*',
|
label: 'Flag enabled',
|
||||||
|
action: `${bold('{{user}}')} enabled ${bold('{{feature}}')} for the ${bold('{{event.environment}}')} environment in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_ENVIRONMENT_VARIANTS_UPDATED]: {
|
[FEATURE_ENVIRONMENT_VARIANTS_UPDATED]: {
|
||||||
action: '*{{user}}* updated variants for *{{feature}}* for the *{{event.environment}}* environment in project *{{project}}*',
|
label: 'Flag variants updated',
|
||||||
|
action: `${bold('{{user}}')} updated variants for ${bold('{{feature}}')} for the ${bold('{{event.environment}}')} environment in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}/variants',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}/variants',
|
||||||
},
|
},
|
||||||
[FEATURE_METADATA_UPDATED]: {
|
[FEATURE_METADATA_UPDATED]: {
|
||||||
action: '*{{user}}* updated *{{feature}}* metadata in project *{{project}}*',
|
label: 'Flag metadata updated',
|
||||||
|
action: `${bold('{{user}}')} updated ${bold('{{feature}}')} metadata in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_COMPLETED]: {
|
[FEATURE_COMPLETED]: {
|
||||||
action: '*{{feature}}* was marked as completed in project *{{project}}*',
|
label: 'Flag marked as completed',
|
||||||
|
action: `${bold('{{feature}}')} was marked as completed in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_POTENTIALLY_STALE_ON]: {
|
[FEATURE_POTENTIALLY_STALE_ON]: {
|
||||||
action: '*{{feature}}* was marked as potentially stale in project *{{project}}*',
|
label: 'Flag potentially stale',
|
||||||
|
action: `${bold('{{feature}}')} was marked as potentially stale in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_PROJECT_CHANGE]: {
|
[FEATURE_PROJECT_CHANGE]: {
|
||||||
action: '*{{user}}* moved *{{feature}}* from *{{event.data.oldProject}}* to *{{project}}*',
|
label: 'Flag moved to a new project',
|
||||||
|
action: `${bold('{{user}}')} moved ${bold('{{feature}}')} from ${bold('{{event.data.oldProject}}')} to ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_REVIVED]: {
|
[FEATURE_REVIVED]: {
|
||||||
action: '*{{user}}* revived *{{feature}}* in project *{{project}}*',
|
label: 'Flag revived',
|
||||||
|
action: `${bold('{{user}}')} revived ${bold('{{feature}}')} in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_STALE_OFF]: {
|
[FEATURE_STALE_OFF]: {
|
||||||
action: '*{{user}}* removed the stale marking on *{{feature}}* in project *{{project}}*',
|
label: 'Flag stale marking removed',
|
||||||
|
action: `${bold('{{user}}')} removed the stale marking on ${bold('{{feature}}')} in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_STALE_ON]: {
|
[FEATURE_STALE_ON]: {
|
||||||
action: '*{{user}}* marked *{{feature}}* as stale in project *{{project}}*',
|
label: 'Flag marked as stale',
|
||||||
|
action: `${bold('{{user}}')} marked ${bold('{{feature}}')} as stale in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_STRATEGY_ADD]: {
|
[FEATURE_STRATEGY_ADD]: {
|
||||||
action: '*{{user}}* added strategy *{{strategyTitle}}* to *{{feature}}* for the *{{event.environment}}* environment in project *{{project}}*',
|
label: 'Flag strategy added',
|
||||||
|
action: `${bold('{{user}}')} added strategy ${bold('{{strategyTitle}}')} to ${bold('{{feature}}')} for the ${bold('{{event.environment}}')} environment in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_STRATEGY_REMOVE]: {
|
[FEATURE_STRATEGY_REMOVE]: {
|
||||||
action: '*{{user}}* removed strategy *{{strategyTitle}}* from *{{feature}}* for the *{{event.environment}}* environment in project *{{project}}*',
|
label: 'Flag strategy removed',
|
||||||
|
action: `${bold('{{user}}')} removed strategy ${bold('{{strategyTitle}}')} from ${bold('{{feature}}')} for the ${bold('{{event.environment}}')} environment in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_STRATEGY_UPDATE]: {
|
[FEATURE_STRATEGY_UPDATE]: {
|
||||||
action: '*{{user}}* updated *{{feature}}* in project *{{project}}* {{strategyChangeText}}',
|
label: 'Flag strategy updated',
|
||||||
|
action: `${bold('{{user}}')} updated ${bold('{{feature}}')} in project ${bold('{{project}}')} {{strategyChangeText}}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_TAGGED]: {
|
[FEATURE_TAGGED]: {
|
||||||
action: '*{{user}}* tagged *{{feature}}* with *{{event.data.type}}:{{event.data.value}}* in project *{{project}}*',
|
label: 'Flag tagged',
|
||||||
|
action: `${bold('{{user}}')} tagged ${bold('{{feature}}')} with ${bold('{{event.data.type}}:{{event.data.value}}')} in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[FEATURE_UNTAGGED]: {
|
[FEATURE_UNTAGGED]: {
|
||||||
action: '*{{user}}* untagged *{{feature}}* with *{{event.preData.type}}:{{event.preData.value}}* in project *{{project}}*',
|
label: 'Flag untagged',
|
||||||
|
action: `${bold('{{user}}')} untagged ${bold('{{feature}}')} with ${bold('{{event.preData.type}}:{{event.preData.value}}')} in project ${bold('{{project}}')}`,
|
||||||
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
path: '/projects/{{event.project}}/features/{{event.featureName}}',
|
||||||
},
|
},
|
||||||
[GROUP_CREATED]: {
|
[GROUP_CREATED]: {
|
||||||
action: '*{{user}}* created group *{{event.data.name}}*',
|
label: 'Group created',
|
||||||
|
action: `${bold('{{user}}')} created group ${bold('{{event.data.name}}')}`,
|
||||||
path: '/admin/groups',
|
path: '/admin/groups',
|
||||||
},
|
},
|
||||||
[GROUP_DELETED]: {
|
[GROUP_DELETED]: {
|
||||||
action: '*{{user}}* deleted group *{{event.preData.name}}*',
|
label: 'Group deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted group ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/admin/groups',
|
path: '/admin/groups',
|
||||||
},
|
},
|
||||||
[GROUP_UPDATED]: {
|
[GROUP_UPDATED]: {
|
||||||
action: '*{{user}}* updated group *{{event.preData.name}}*',
|
label: 'Group updated',
|
||||||
|
action: `${bold('{{user}}')} updated group ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/admin/groups',
|
path: '/admin/groups',
|
||||||
},
|
},
|
||||||
[BANNER_CREATED]: {
|
[BANNER_CREATED]: {
|
||||||
action: '*{{user}}* created banner *{{event.data.message}}*',
|
label: 'Banner created',
|
||||||
|
action: `${bold('{{user}}')} created banner ${bold('{{event.data.message}}')}`,
|
||||||
path: '/admin/message-banners',
|
path: '/admin/message-banners',
|
||||||
},
|
},
|
||||||
[BANNER_DELETED]: {
|
[BANNER_DELETED]: {
|
||||||
action: '*{{user}}* deleted banner *{{event.preData.message}}*',
|
label: 'Banner deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted banner ${bold('{{event.preData.message}}')}`,
|
||||||
path: '/admin/message-banners',
|
path: '/admin/message-banners',
|
||||||
},
|
},
|
||||||
[BANNER_UPDATED]: {
|
[BANNER_UPDATED]: {
|
||||||
action: '*{{user}}* updated banner *{{event.preData.message}}*',
|
label: 'Banner updated',
|
||||||
|
action: `${bold('{{user}}')} updated banner ${bold('{{event.preData.message}}')}`,
|
||||||
path: '/admin/message-banners',
|
path: '/admin/message-banners',
|
||||||
},
|
},
|
||||||
[PROJECT_CREATED]: {
|
[PROJECT_CREATED]: {
|
||||||
action: '*{{user}}* created project *{{project}}*',
|
label: 'Project created',
|
||||||
|
action: `${bold('{{user}}')} created project ${bold('{{project}}')}`,
|
||||||
path: '/projects',
|
path: '/projects',
|
||||||
},
|
},
|
||||||
[PROJECT_DELETED]: {
|
[PROJECT_DELETED]: {
|
||||||
action: '*{{user}}* deleted project *{{event.project}}*',
|
label: 'Project deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted project ${bold('{{event.project}}')}`,
|
||||||
path: '/projects',
|
path: '/projects',
|
||||||
},
|
},
|
||||||
[SEGMENT_CREATED]: {
|
[SEGMENT_CREATED]: {
|
||||||
action: '*{{user}}* created segment *{{event.data.name}}*',
|
label: 'Segment created',
|
||||||
|
action: `${bold('{{user}}')} created segment ${bold('{{event.data.name}}')}`,
|
||||||
path: '/segments',
|
path: '/segments',
|
||||||
},
|
},
|
||||||
[SEGMENT_DELETED]: {
|
[SEGMENT_DELETED]: {
|
||||||
action: '*{{user}}* deleted segment *{{event.preData.name}}*',
|
label: 'Segment deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted segment ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/segments',
|
path: '/segments',
|
||||||
},
|
},
|
||||||
[SEGMENT_UPDATED]: {
|
[SEGMENT_UPDATED]: {
|
||||||
action: '*{{user}}* updated segment *{{event.preData.name}}*',
|
label: 'Segment updated',
|
||||||
|
action: `${bold('{{user}}')} updated segment ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/segments',
|
path: '/segments',
|
||||||
},
|
},
|
||||||
[SERVICE_ACCOUNT_CREATED]: {
|
[SERVICE_ACCOUNT_CREATED]: {
|
||||||
action: '*{{user}}* created service account *{{event.data.name}}*',
|
label: 'Service account created',
|
||||||
|
action: `${bold('{{user}}')} created service account ${bold('{{event.data.name}}')}`,
|
||||||
path: '/admin/service-accounts',
|
path: '/admin/service-accounts',
|
||||||
},
|
},
|
||||||
[SERVICE_ACCOUNT_DELETED]: {
|
[SERVICE_ACCOUNT_DELETED]: {
|
||||||
action: '*{{user}}* deleted service account *{{event.preData.name}}*',
|
label: 'Service account deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted service account ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/admin/service-accounts',
|
path: '/admin/service-accounts',
|
||||||
},
|
},
|
||||||
[SERVICE_ACCOUNT_UPDATED]: {
|
[SERVICE_ACCOUNT_UPDATED]: {
|
||||||
action: '*{{user}}* updated service account *{{event.preData.name}}*',
|
label: 'Service account updated',
|
||||||
|
action: `${bold('{{user}}')} updated service account ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/admin/service-accounts',
|
path: '/admin/service-accounts',
|
||||||
},
|
},
|
||||||
[USER_CREATED]: {
|
[USER_CREATED]: {
|
||||||
action: '*{{user}}* created user *{{event.data.name}}*',
|
label: 'User created',
|
||||||
|
action: `${bold('{{user}}')} created user ${bold('{{event.data.name}}')}`,
|
||||||
path: '/admin/users',
|
path: '/admin/users',
|
||||||
},
|
},
|
||||||
[USER_DELETED]: {
|
[USER_DELETED]: {
|
||||||
action: '*{{user}}* deleted user *{{event.preData.name}}*',
|
label: 'User deleted',
|
||||||
|
action: `${bold('{{user}}')} deleted user ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/admin/users',
|
path: '/admin/users',
|
||||||
},
|
},
|
||||||
[USER_UPDATED]: {
|
[USER_UPDATED]: {
|
||||||
action: '*{{user}}* updated user *{{event.preData.name}}*',
|
label: 'User updated',
|
||||||
|
action: `${bold('{{user}}')} updated user ${bold('{{event.preData.name}}')}`,
|
||||||
path: '/admin/users',
|
path: '/admin/users',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -334,17 +396,19 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
const text = `#${changeRequestId}`;
|
const text = `#${changeRequestId}`;
|
||||||
const featureLink = this.generateFeatureLink(event);
|
const featureLink = this.generateFeatureLink(event);
|
||||||
const featureText = featureLink
|
const featureText = featureLink
|
||||||
? ` for feature flag *${featureLink}*`
|
? ` for feature flag ${bold(featureLink)}`
|
||||||
: '';
|
: '';
|
||||||
const environmentText = environment
|
const environmentText = environment
|
||||||
? ` in the *${environment}* environment`
|
? ` in the ${bold(environment)} environment`
|
||||||
: '';
|
: '';
|
||||||
const projectLink = this.generateProjectLink(event);
|
const projectLink = this.generateProjectLink(event);
|
||||||
const projectText = project ? ` in project *${projectLink}*` : '';
|
const projectText = project
|
||||||
|
? ` in project ${bold(projectLink)}`
|
||||||
|
: '';
|
||||||
if (this.linkStyle === LinkStyle.SLACK) {
|
if (this.linkStyle === LinkStyle.SLACK) {
|
||||||
return `*<${url}|${text}>*${featureText}${environmentText}${projectText}`;
|
return `${bold(`<${url}|${text}>`)}${featureText}${environmentText}${projectText}`;
|
||||||
} else {
|
} else {
|
||||||
return `*[${text}](${url})*${featureText}${environmentText}${projectText}`;
|
return `${bold(`[${text}](${url})`)}${featureText}${environmentText}${projectText}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -410,9 +474,9 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
event,
|
event,
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return `by updating strategy *${this.getStrategyTitle(
|
return `by updating strategy ${bold(
|
||||||
event,
|
this.getStrategyTitle(event),
|
||||||
)}* in *${environment}*`;
|
)} in ${bold(environment)}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -462,9 +526,9 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
const strategySpecificText = [usersText, constraintText, segmentsText]
|
const strategySpecificText = [usersText, constraintText, segmentsText]
|
||||||
.filter((x) => x.length)
|
.filter((x) => x.length)
|
||||||
.join(';');
|
.join(';');
|
||||||
return `by updating strategy *${this.getStrategyTitle(
|
return `by updating strategy ${bold(
|
||||||
event,
|
this.getStrategyTitle(event),
|
||||||
)}* in *${environment}*${strategySpecificText}`;
|
)} in ${bold(environment)}${strategySpecificText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private flexibleRolloutStrategyChangeText(event: IEvent) {
|
private flexibleRolloutStrategyChangeText(event: IEvent) {
|
||||||
@ -510,9 +574,9 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
]
|
]
|
||||||
.filter((txt) => txt.length)
|
.filter((txt) => txt.length)
|
||||||
.join(';');
|
.join(';');
|
||||||
return `by updating strategy *${this.getStrategyTitle(
|
return `by updating strategy ${bold(
|
||||||
event,
|
this.getStrategyTitle(event),
|
||||||
)}* in *${environment}*${strategySpecificText}`;
|
)} in ${bold(environment)}${strategySpecificText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private defaultStrategyChangeText(event: IEvent) {
|
private defaultStrategyChangeText(event: IEvent) {
|
||||||
@ -528,9 +592,9 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
const strategySpecificText = [constraintText, segmentsText]
|
const strategySpecificText = [constraintText, segmentsText]
|
||||||
.filter((txt) => txt.length)
|
.filter((txt) => txt.length)
|
||||||
.join(';');
|
.join(';');
|
||||||
return `by updating strategy *${this.getStrategyTitle(
|
return `by updating strategy ${bold(
|
||||||
event,
|
this.getStrategyTitle(event),
|
||||||
)}* in *${environment}*${strategySpecificText}`;
|
)} in ${bold(environment)}${strategySpecificText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private constraintChangeText(
|
private constraintChangeText(
|
||||||
@ -598,13 +662,10 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
: ` segments from ${oldSegmentsText} to ${newSegmentsText}`;
|
: ` segments from ${oldSegmentsText} to ${newSegmentsText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
format(event: IEvent): {
|
format(event: IEvent): IFormattedEventData {
|
||||||
text: string;
|
|
||||||
url?: string;
|
|
||||||
} {
|
|
||||||
const { createdBy, type } = event;
|
const { createdBy, type } = event;
|
||||||
const { action, path } = EVENT_MAP[type] || {
|
const { action, path } = EVENT_MAP[type] || {
|
||||||
action: `triggered *${type}*`,
|
action: `triggered ${bold(type)}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
@ -619,12 +680,14 @@ export class FeatureEventFormatterMd implements FeatureEventFormatter {
|
|||||||
|
|
||||||
Mustache.escape = (text) => text;
|
Mustache.escape = (text) => text;
|
||||||
|
|
||||||
|
const label = EVENT_MAP[type]?.label || type;
|
||||||
const text = Mustache.render(action, context);
|
const text = Mustache.render(action, context);
|
||||||
const url = path
|
const url = path
|
||||||
? `${this.unleashUrl}${Mustache.render(path, context)}`
|
? `${this.unleashUrl}${Mustache.render(path, context)}`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
label,
|
||||||
text,
|
text,
|
||||||
url,
|
url,
|
||||||
};
|
};
|
||||||
|
@ -18,8 +18,12 @@ import {
|
|||||||
import { normalizeQueryParams } from '../../features/feature-search/search-utils';
|
import { normalizeQueryParams } from '../../features/feature-search/search-utils';
|
||||||
import Controller from '../../routes/controller';
|
import Controller from '../../routes/controller';
|
||||||
import type { IAuthRequest } from '../../server-impl';
|
import type { IAuthRequest } from '../../server-impl';
|
||||||
import type { IEvent } from '../../types';
|
import type { IEnrichedEvent, IEvent } from '../../types';
|
||||||
import { anonymiseKeys, extractUserIdFromUser } from '../../util';
|
import { anonymiseKeys, extractUserIdFromUser } from '../../util';
|
||||||
|
import {
|
||||||
|
FeatureEventFormatterMd,
|
||||||
|
type FeatureEventFormatter,
|
||||||
|
} from '../../addons/feature-event-formatter-md';
|
||||||
|
|
||||||
const ANON_KEYS = ['email', 'username', 'createdBy'];
|
const ANON_KEYS = ['email', 'username', 'createdBy'];
|
||||||
const version = 1 as const;
|
const version = 1 as const;
|
||||||
@ -28,6 +32,8 @@ export default class EventSearchController extends Controller {
|
|||||||
|
|
||||||
private flagResolver: IFlagResolver;
|
private flagResolver: IFlagResolver;
|
||||||
|
|
||||||
|
private msgFormatter: FeatureEventFormatter;
|
||||||
|
|
||||||
private openApiService: OpenApiService;
|
private openApiService: OpenApiService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -41,6 +47,9 @@ export default class EventSearchController extends Controller {
|
|||||||
this.eventService = eventService;
|
this.eventService = eventService;
|
||||||
this.flagResolver = config.flagResolver;
|
this.flagResolver = config.flagResolver;
|
||||||
this.openApiService = openApiService;
|
this.openApiService = openApiService;
|
||||||
|
this.msgFormatter = new FeatureEventFormatterMd(
|
||||||
|
config.server.unleashUrl,
|
||||||
|
);
|
||||||
|
|
||||||
this.route({
|
this.route({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -85,17 +94,37 @@ export default class EventSearchController extends Controller {
|
|||||||
extractUserIdFromUser(user),
|
extractUserIdFromUser(user),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const enrichedEvents = this.enrichEvents(events);
|
||||||
|
|
||||||
this.openApiService.respondWithValidation(
|
this.openApiService.respondWithValidation(
|
||||||
200,
|
200,
|
||||||
res,
|
res,
|
||||||
eventSearchResponseSchema.$id,
|
eventSearchResponseSchema.$id,
|
||||||
serializeDates({
|
serializeDates({
|
||||||
events: serializeDates(this.maybeAnonymiseEvents(events)),
|
events: serializeDates(
|
||||||
|
this.maybeAnonymiseEvents(enrichedEvents),
|
||||||
|
),
|
||||||
total: totalEvents,
|
total: totalEvents,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enrichEvents(events: IEvent[]): IEvent[] | IEnrichedEvent[] {
|
||||||
|
if (this.flagResolver.isEnabled('eventTimeline')) {
|
||||||
|
return events.map((event) => {
|
||||||
|
const { label, text: description } =
|
||||||
|
this.msgFormatter.format(event);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...event,
|
||||||
|
label,
|
||||||
|
description,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
|
||||||
maybeAnonymiseEvents(events: IEvent[]): IEvent[] {
|
maybeAnonymiseEvents(events: IEvent[]): IEvent[] {
|
||||||
if (this.flagResolver.isEnabled('anonymiseEventLog')) {
|
if (this.flagResolver.isEnabled('anonymiseEventLog')) {
|
||||||
return anonymiseKeys(events, ANON_KEYS);
|
return anonymiseKeys(events, ANON_KEYS);
|
||||||
|
@ -92,6 +92,17 @@ export const eventSchema = {
|
|||||||
nullable: true,
|
nullable: true,
|
||||||
description: 'Any tags related to the event, if applicable.',
|
description: 'Any tags related to the event, if applicable.',
|
||||||
},
|
},
|
||||||
|
label: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'A concise, human-readable name for the event.',
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description:
|
||||||
|
'A detailed description of the event, formatted in markdown.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
schemas: {
|
schemas: {
|
||||||
|
@ -374,6 +374,11 @@ export interface IEvent extends Omit<IBaseEvent, 'ip'> {
|
|||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IEnrichedEvent extends IEvent {
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IEventList {
|
export interface IEventList {
|
||||||
totalEvents: number;
|
totalEvents: number;
|
||||||
events: IEvent[];
|
events: IEvent[];
|
||||||
|
Loading…
Reference in New Issue
Block a user