feat: create alerts is updated from trace explorer (#2995)

This commit is contained in:
Palash Gupta 2023-06-30 11:18:12 +05:30 committed by GitHub
parent 709bfda0cc
commit cda37b99b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 139 additions and 51 deletions

View File

@ -0,0 +1,27 @@
import { TFunction } from 'i18next';
import { AlertTypes } from 'types/api/alerts/alertTypes';
import { OptionType } from './types';
export const getOptionList = (t: TFunction): OptionType[] => [
{
title: t('metric_based_alert'),
selection: AlertTypes.METRICS_BASED_ALERT,
description: t('metric_based_alert_desc'),
},
{
title: t('log_based_alert'),
selection: AlertTypes.LOGS_BASED_ALERT,
description: t('log_based_alert_desc'),
},
{
title: t('traces_based_alert'),
selection: AlertTypes.TRACES_BASED_ALERT,
description: t('traces_based_alert_desc'),
},
{
title: t('exceptions_based_alert'),
selection: AlertTypes.EXCEPTIONS_BASED_ALERT,
description: t('exceptions_based_alert_desc'),
},
];

View File

@ -1,61 +1,40 @@
import { Row } from 'antd'; import { Row } from 'antd';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { AlertTypes } from 'types/api/alerts/alertTypes'; import { AlertTypes } from 'types/api/alerts/alertTypes';
import { getOptionList } from './config';
import { AlertTypeCard, SelectTypeContainer } from './styles'; import { AlertTypeCard, SelectTypeContainer } from './styles';
import { OptionType } from './types';
interface OptionType {
title: string;
selection: AlertTypes;
description: string;
}
function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element { function SelectAlertType({ onSelect }: SelectAlertTypeProps): JSX.Element {
const { t } = useTranslation(['alerts']); const { t } = useTranslation(['alerts']);
const renderOptions = (): JSX.Element => { const optionList = getOptionList(t);
const optionList: OptionType[] = [
{ const renderOptions = useMemo(
title: t('metric_based_alert'), () => (
selection: AlertTypes.METRICS_BASED_ALERT,
description: t('metric_based_alert_desc'),
},
{
title: t('log_based_alert'),
selection: AlertTypes.LOGS_BASED_ALERT,
description: t('log_based_alert_desc'),
},
{
title: t('traces_based_alert'),
selection: AlertTypes.TRACES_BASED_ALERT,
description: t('traces_based_alert_desc'),
},
{
title: t('exceptions_based_alert'),
selection: AlertTypes.EXCEPTIONS_BASED_ALERT,
description: t('exceptions_based_alert_desc'),
},
];
return (
<> <>
{optionList.map((o: OptionType) => ( {optionList.map((option: OptionType) => (
<AlertTypeCard <AlertTypeCard
key={o.selection} key={option.selection}
title={o.title} title={option.title}
onClick={(): void => { onClick={(): void => {
onSelect(o.selection); onSelect(option.selection);
}} }}
> >
{o.description} {option.description}
</AlertTypeCard> </AlertTypeCard>
))} ))}
</> </>
); ),
}; [onSelect, optionList],
);
return ( return (
<SelectTypeContainer> <SelectTypeContainer>
<h3> {t('choose_alert_type')} </h3> <h3> {t('choose_alert_type')} </h3>
<Row>{renderOptions()}</Row> <Row>{renderOptions}</Row>
</SelectTypeContainer> </SelectTypeContainer>
); );
} }

View File

@ -0,0 +1,7 @@
import { AlertTypes } from 'types/api/alerts/alertTypes';
export interface OptionType {
title: string;
selection: AlertTypes;
description: string;
}

View File

@ -0,0 +1,8 @@
import { AlertTypes } from 'types/api/alerts/alertTypes';
import { DataSource } from 'types/common/queryBuilder';
export const ALERT_TYPE_VS_SOURCE_MAPPING = {
[DataSource.LOGS]: AlertTypes.LOGS_BASED_ALERT,
[DataSource.METRICS]: AlertTypes.METRICS_BASED_ALERT,
[DataSource.TRACES]: AlertTypes.TRACES_BASED_ALERT,
};

View File

@ -1,9 +1,11 @@
import { Form, Row } from 'antd'; import { Form, Row } from 'antd';
import FormAlertRules from 'container/FormAlertRules'; import FormAlertRules from 'container/FormAlertRules';
import { useState } from 'react'; import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
import { useEffect, useState } from 'react';
import { AlertTypes } from 'types/api/alerts/alertTypes'; import { AlertTypes } from 'types/api/alerts/alertTypes';
import { AlertDef } from 'types/api/alerts/def'; import { AlertDef } from 'types/api/alerts/def';
import { ALERT_TYPE_VS_SOURCE_MAPPING } from './config';
import { import {
alertDefaults, alertDefaults,
exceptionAlertDefaults, exceptionAlertDefaults,
@ -17,6 +19,9 @@ function CreateRules(): JSX.Element {
const [alertType, setAlertType] = useState<AlertTypes>( const [alertType, setAlertType] = useState<AlertTypes>(
AlertTypes.METRICS_BASED_ALERT, AlertTypes.METRICS_BASED_ALERT,
); );
const compositeQuery = useGetCompositeQueryParam();
const [formInstance] = Form.useForm(); const [formInstance] = Form.useForm();
const onSelectType = (typ: AlertTypes): void => { const onSelectType = (typ: AlertTypes): void => {
@ -36,6 +41,19 @@ function CreateRules(): JSX.Element {
} }
}; };
useEffect(() => {
if (!compositeQuery) {
return;
}
const dataSource = compositeQuery?.builder?.queryData[0]?.dataSource;
const alertType = ALERT_TYPE_VS_SOURCE_MAPPING[dataSource];
if (alertType) {
onSelectType(alertType);
}
}, [compositeQuery]);
if (!initValues) { if (!initValues) {
return ( return (
<Row wrap={false}> <Row wrap={false}>

View File

@ -1,24 +1,44 @@
import { Button, Dropdown, MenuProps, Modal } from 'antd'; import { Button, Dropdown, MenuProps, Modal } from 'antd';
import { COMPOSITE_QUERY } from 'constants/queryBuilderQueryNames';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { Dashboard } from 'types/api/dashboard/getAll'; import { Dashboard } from 'types/api/dashboard/getAll';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { MENU_KEY, MENU_LABEL } from './config'; import { MENU_KEY, MENU_LABEL } from './config';
import ExportPanelContainer from './ExportPanel'; import ExportPanelContainer from './ExportPanel';
function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element { function ExportPanel({
isLoading,
onExport,
query,
}: ExportPanelProps): JSX.Element {
const [isExport, setIsExport] = useState<boolean>(false); const [isExport, setIsExport] = useState<boolean>(false);
const onModalToggle = useCallback((value: boolean) => { const onModalToggle = useCallback((value: boolean) => {
setIsExport(value); setIsExport(value);
}, []); }, []);
const onCreateAlertsHandler = useCallback(() => {
history.push(
`${ROUTES.ALERTS_NEW}?${COMPOSITE_QUERY}=${encodeURIComponent(
JSON.stringify(query),
)}`,
);
}, [query]);
const onMenuClickHandler: MenuProps['onClick'] = useCallback( const onMenuClickHandler: MenuProps['onClick'] = useCallback(
(e: OnClickProps) => { (e: OnClickProps) => {
if (e.key === MENU_KEY.EXPORT) { if (e.key === MENU_KEY.EXPORT) {
onModalToggle(true); onModalToggle(true);
} }
if (e.key === MENU_KEY.CREATE_ALERTS) {
onCreateAlertsHandler();
}
}, },
[onModalToggle], [onModalToggle, onCreateAlertsHandler],
); );
const menu: MenuProps = useMemo( const menu: MenuProps = useMemo(
@ -54,7 +74,11 @@ function ExportPanel({ isLoading, onExport }: ExportPanelProps): JSX.Element {
open={isExport} open={isExport}
centered centered
> >
<ExportPanelContainer isLoading={isLoading} onExport={onExport} /> <ExportPanelContainer
query={query}
isLoading={isLoading}
onExport={onExport}
/>
</Modal> </Modal>
</> </>
); );
@ -71,6 +95,7 @@ interface OnClickProps {
export interface ExportPanelProps { export interface ExportPanelProps {
isLoading?: boolean; isLoading?: boolean;
onExport: (dashboard: Dashboard | null) => void; onExport: (dashboard: Dashboard | null) => void;
query: Query | null;
} }
export default ExportPanel; export default ExportPanel;

View File

@ -64,7 +64,7 @@ function WidgetHeader({
history.push( history.push(
`${window.location.pathname}/new?widgetId=${widgetId}&graphType=${ `${window.location.pathname}/new?widgetId=${widgetId}&graphType=${
widget.panelTypes widget.panelTypes
}&${COMPOSITE_QUERY}=${JSON.stringify(widget.query)}`, }&${COMPOSITE_QUERY}=${encodeURIComponent(JSON.stringify(widget.query))}`,
); );
}, [widget.id, widget.panelTypes, widget.query]); }, [widget.id, widget.panelTypes, widget.query]);

View File

@ -77,8 +77,8 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
history.push( history.push(
`${ `${
ROUTES.EDIT_ALERTS ROUTES.EDIT_ALERTS
}?ruleId=${record.id.toString()}&${COMPOSITE_QUERY}=${JSON.stringify( }?ruleId=${record.id.toString()}&${COMPOSITE_QUERY}=${encodeURIComponent(
compositeQuery, JSON.stringify(compositeQuery),
)}`, )}`,
); );
}) })

View File

@ -47,7 +47,9 @@ function DashboardGraphSlider({ toggleAddWidget }: Props): JSX.Element {
history.push( history.push(
`${history.location.pathname}/new?graphType=${name}&widgetId=${ `${history.location.pathname}/new?graphType=${name}&widgetId=${
emptyLayout.i emptyLayout.i
}&${COMPOSITE_QUERY}=${JSON.stringify(initialQueriesMap.metrics)}`, }&${COMPOSITE_QUERY}=${encodeURIComponent(
JSON.stringify(initialQueriesMap.metrics),
)}`,
); );
} catch (error) { } catch (error) {
notifications.error({ notifications.error({

View File

@ -8,7 +8,16 @@ export const useGetCompositeQueryParam = (): Query | null => {
return useMemo(() => { return useMemo(() => {
const compositeQuery = urlQuery.get(COMPOSITE_QUERY); const compositeQuery = urlQuery.get(COMPOSITE_QUERY);
let parsedCompositeQuery: Query | null = null;
return compositeQuery ? JSON.parse(compositeQuery) : null; try {
if (!compositeQuery) return null;
parsedCompositeQuery = JSON.parse(decodeURIComponent(compositeQuery));
} catch (e) {
parsedCompositeQuery = null;
}
return parsedCompositeQuery;
}, [urlQuery]); }, [urlQuery]);
}; };

View File

@ -74,7 +74,9 @@ function TracesExplorer(): JSX.Element {
dashboardId: data?.payload?.uuid, dashboardId: data?.payload?.uuid,
})}/new?${QueryParams.graphType}=graph&${ })}/new?${QueryParams.graphType}=graph&${
QueryParams.widgetId QueryParams.widgetId
}=empty&${COMPOSITE_QUERY}=${JSON.stringify(exportDefaultQuery)}`; }=empty&${COMPOSITE_QUERY}=${encodeURIComponent(
JSON.stringify(exportDefaultQuery),
)}`;
history.push(dashboardEditView); history.push(dashboardEditView);
}, },
@ -118,7 +120,11 @@ function TracesExplorer(): JSX.Element {
<Container> <Container>
<ActionsWrapper> <ActionsWrapper>
<ExportPanel isLoading={isLoading} onExport={handleExport} /> <ExportPanel
query={stagedQuery}
isLoading={isLoading}
onExport={handleExport}
/>
</ActionsWrapper> </ActionsWrapper>
<Tabs <Tabs

View File

@ -460,7 +460,10 @@ export function QueryBuilderProvider({
id: uuid(), id: uuid(),
}; };
urlQuery.set(COMPOSITE_QUERY, JSON.stringify(currentGeneratedQuery)); urlQuery.set(
COMPOSITE_QUERY,
encodeURIComponent(JSON.stringify(currentGeneratedQuery)),
);
if (searchParams) { if (searchParams) {
Object.keys(searchParams).forEach((param) => Object.keys(searchParams).forEach((param) =>

View File

@ -91,7 +91,11 @@ export const SaveDashboard = ({
const compositeQuery = params.get(COMPOSITE_QUERY); const compositeQuery = params.get(COMPOSITE_QUERY);
const { maxTime, minTime } = store.getState().globalTime; const { maxTime, minTime } = store.getState().globalTime;
const query = compositeQuery const query = compositeQuery
? updateStepInterval(JSON.parse(compositeQuery), maxTime, minTime) ? updateStepInterval(
JSON.parse(decodeURIComponent(compositeQuery)),
maxTime,
minTime,
)
: updateStepInterval(selectedWidget.query, maxTime, minTime); : updateStepInterval(selectedWidget.query, maxTime, minTime);
const response = await updateDashboardApi({ const response = await updateDashboardApi({