feat: disable alerts feature (UI) (#1445)

* feat: added enable disable feature for rules

* fix: resolved type issue in getTriggered

Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
Amol Umbark 2022-08-04 11:55:09 +05:30 committed by GitHub
parent a6ed6c03c1
commit 5dc6d28f2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 261 additions and 73 deletions

View File

@ -0,0 +1,26 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/alerts/patch';
const patch = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.patch(`/rules/${props.id}`, {
...props.data,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default patch;

View File

@ -1,10 +1,11 @@
import { Button } from 'antd';
import { NotificationInstance } from 'antd/lib/notification/index';
import deleteAlerts from 'api/alerts/delete';
import { State } from 'hooks/useFetch';
import React, { useState } from 'react';
import { PayloadProps as DeleteAlertPayloadProps } from 'types/api/alerts/delete';
import { Alerts } from 'types/api/alerts/getAll';
import { GettableAlert } from 'types/api/alerts/get';
import { ColumnButton } from './styles';
function DeleteAlert({
id,
@ -72,20 +73,20 @@ function DeleteAlert({
};
return (
<Button
<ColumnButton
disabled={deleteAlertState.loading || false}
loading={deleteAlertState.loading || false}
onClick={(): Promise<void> => onDeleteHandler(id)}
type="link"
>
Delete
</Button>
</ColumnButton>
);
}
interface DeleteAlertProps {
id: Alerts['id'];
setData: React.Dispatch<React.SetStateAction<Alerts[]>>;
id: GettableAlert['id'];
setData: React.Dispatch<React.SetStateAction<GettableAlert[]>>;
notifications: NotificationInstance;
}

View File

@ -1,6 +1,6 @@
/* eslint-disable react/display-name */
import { PlusOutlined } from '@ant-design/icons';
import { notification, Tag, Typography } from 'antd';
import { notification, Typography } from 'antd';
import Table, { ColumnsType } from 'antd/lib/table';
import TextToolTip from 'components/TextToolTip';
import ROUTES from 'constants/routes';
@ -13,15 +13,16 @@ import { UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { Alerts } from 'types/api/alerts/getAll';
import { GettableAlert } from 'types/api/alerts/get';
import AppReducer from 'types/reducer/app';
import DeleteAlert from './DeleteAlert';
import { Button, ButtonContainer } from './styles';
import { Button, ButtonContainer, ColumnButton, StyledTag } from './styles';
import Status from './TableComponents/Status';
import ToggleAlertState from './ToggleAlertState';
function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
const [data, setData] = useState<Alerts[]>(allAlertRules || []);
const [data, setData] = useState<GettableAlert[]>(allAlertRules || []);
const { t } = useTranslation('common');
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
const [addNewAlert, action] = useComponentPermission(
@ -53,22 +54,27 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
history.push(`${ROUTES.EDIT_ALERTS}?ruleId=${id}`);
};
const columns: ColumnsType<Alerts> = [
const columns: ColumnsType<GettableAlert> = [
{
title: 'Status',
dataIndex: 'state',
key: 'state',
sorter: (a, b): number =>
b.labels.severity.length - a.labels.severity.length,
(b.state ? b.state.charCodeAt(0) : 1000) -
(a.state ? a.state.charCodeAt(0) : 1000),
render: (value): JSX.Element => <Status status={value} />,
},
{
title: 'Alert Name',
dataIndex: 'alert',
key: 'name',
sorter: (a, b): number => a.name.charCodeAt(0) - b.name.charCodeAt(0),
sorter: (a, b): number =>
(a.alert ? a.alert.charCodeAt(0) : 1000) -
(b.alert ? b.alert.charCodeAt(0) : 1000),
render: (value, record): JSX.Element => (
<Typography.Link onClick={(): void => onEditHandler(record.id.toString())}>
<Typography.Link
onClick={(): void => onEditHandler(record.id ? record.id.toString() : '')}
>
{value}
</Typography.Link>
),
@ -78,7 +84,8 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
dataIndex: 'labels',
key: 'severity',
sorter: (a, b): number =>
a.labels.severity.length - b.labels.severity.length,
(a.labels ? a.labels.severity.length : 0) -
(b.labels ? b.labels.severity.length : 0),
render: (value): JSX.Element => {
const objectKeys = Object.keys(value);
const withSeverityKey = objectKeys.find((e) => e === 'severity') || '';
@ -92,6 +99,7 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
dataIndex: 'labels',
key: 'tags',
align: 'center',
width: 350,
render: (value): JSX.Element => {
const objectKeys = Object.keys(value);
const withOutSeverityKeys = objectKeys.filter((e) => e !== 'severity');
@ -104,9 +112,9 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
<>
{withOutSeverityKeys.map((e) => {
return (
<Tag key={e} color="magenta">
<StyledTag key={e} color="magenta">
{e}: {value[e]}
</Tag>
</StyledTag>
);
})}
</>
@ -120,14 +128,19 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
title: 'Action',
dataIndex: 'id',
key: 'action',
render: (id: Alerts['id']): JSX.Element => {
render: (id: GettableAlert['id'], record): JSX.Element => {
return (
<>
<DeleteAlert notifications={notifications} setData={setData} id={id} />
<ToggleAlertState disabled={record.disabled} setData={setData} id={id} />
<Button onClick={(): void => onEditHandler(id.toString())} type="link">
<ColumnButton
onClick={(): void => onEditHandler(id.toString())}
type="link"
>
Edit
</Button>
</ColumnButton>
<DeleteAlert notifications={notifications} setData={setData} id={id} />
</>
);
},
@ -159,8 +172,10 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
}
interface ListAlertProps {
allAlertRules: Alerts[];
refetch: UseQueryResult<ErrorResponse | SuccessResponse<Alerts[]>>['refetch'];
allAlertRules: GettableAlert[];
refetch: UseQueryResult<
ErrorResponse | SuccessResponse<GettableAlert[]>
>['refetch'];
}
export default ListAlert;

View File

@ -1,6 +1,6 @@
import { Tag } from 'antd';
import React from 'react';
import { Alerts } from 'types/api/alerts/getAll';
import { GettableAlert } from 'types/api/alerts/get';
function Status({ status }: StatusProps): JSX.Element {
switch (status) {
@ -16,14 +16,18 @@ function Status({ status }: StatusProps): JSX.Element {
return <Tag color="red">Firing</Tag>;
}
case 'disabled': {
return <Tag>Disabled</Tag>;
}
default: {
return <Tag color="default">Unknown Status</Tag>;
return <Tag color="default">Unknown</Tag>;
}
}
}
interface StatusProps {
status: Alerts['state'];
status: GettableAlert['state'];
}
export default Status;

View File

@ -0,0 +1,108 @@
import { notification } from 'antd';
import patchAlert from 'api/alerts/patch';
import { State } from 'hooks/useFetch';
import React, { useState } from 'react';
import { GettableAlert } from 'types/api/alerts/get';
import { PayloadProps as PatchPayloadProps } from 'types/api/alerts/patch';
import { ColumnButton } from './styles';
function ToggleAlertState({
id,
disabled,
setData,
}: ToggleAlertStateProps): JSX.Element {
const [apiStatus, setAPIStatus] = useState<State<PatchPayloadProps>>({
error: false,
errorMessage: '',
loading: false,
success: false,
payload: undefined,
});
const defaultErrorMessage = 'Something went wrong';
const onToggleHandler = async (
id: number,
disabled: boolean,
): Promise<void> => {
try {
setAPIStatus((state) => ({
...state,
loading: true,
}));
const response = await patchAlert({
id,
data: {
disabled,
},
});
if (response.statusCode === 200) {
setData((state) => {
return state.map((alert) => {
if (alert.id === id) {
return {
...alert,
disabled: response.payload.disabled,
state: response.payload.state,
};
}
return alert;
});
});
setAPIStatus((state) => ({
...state,
loading: false,
payload: response.payload,
}));
notification.success({
message: 'Success',
});
} else {
setAPIStatus((state) => ({
...state,
loading: false,
error: true,
errorMessage: response.error || defaultErrorMessage,
}));
notification.error({
message: response.error || defaultErrorMessage,
});
}
} catch (error) {
setAPIStatus((state) => ({
...state,
loading: false,
error: true,
errorMessage: defaultErrorMessage,
}));
notification.error({
message: defaultErrorMessage,
});
}
};
return (
<ColumnButton
disabled={apiStatus.loading || false}
loading={apiStatus.loading || false}
onClick={(): Promise<void> => onToggleHandler(id, !disabled)}
type="link"
>
{disabled ? 'Enable' : 'Disable'}
</ColumnButton>
);
}
interface ToggleAlertStateProps {
id: GettableAlert['id'];
disabled: boolean;
setData: React.Dispatch<React.SetStateAction<GettableAlert[]>>;
}
export default ToggleAlertState;

View File

@ -1,4 +1,4 @@
import { Button as ButtonComponent } from 'antd';
import { Button as ButtonComponent, Tag } from 'antd';
import styled from 'styled-components';
export const ButtonContainer = styled.div`
@ -12,6 +12,20 @@ export const ButtonContainer = styled.div`
export const Button = styled(ButtonComponent)`
&&& {
margin-left: 1rem;
margin-left: 1em;
}
`;
export const ColumnButton = styled(ButtonComponent)`
&&& {
padding-left: 0;
padding-right: 0;
margin-right: 1.5em;
}
`;
export const StyledTag = styled(Tag)`
&&& {
white-space: normal;
}
`;

View File

@ -2,7 +2,7 @@
import type { SelectProps } from 'antd';
import { Tag } from 'antd';
import React, { useCallback, useMemo } from 'react';
import { Alerts } from 'types/api/alerts/getAll';
import { Alerts } from 'types/api/alerts/getTriggered';
import { Container, Select } from './styles';

View File

@ -2,7 +2,7 @@ import { Tag, Typography } from 'antd';
import convertDateToAmAndPm from 'lib/convertDateToAmAndPm';
import getFormattedDate from 'lib/getFormatedDate';
import React from 'react';
import { Alerts } from 'types/api/alerts/getAll';
import { Alerts } from 'types/api/alerts/getTriggered';
import Status from '../TableComponents/AlertStatus';
import { TableCell, TableRow } from './styles';

View File

@ -1,7 +1,7 @@
import { MinusSquareOutlined, PlusSquareOutlined } from '@ant-design/icons';
import { Tag } from 'antd';
import React, { useState } from 'react';
import { Alerts } from 'types/api/alerts/getAll';
import { Alerts } from 'types/api/alerts/getTriggered';
import ExapandableRow from './ExapandableRow';
import { IconContainer, StatusContainer, TableCell, TableRow } from './styles';

View File

@ -1,6 +1,6 @@
import groupBy from 'lodash-es/groupBy';
import React, { useMemo } from 'react';
import { Alerts } from 'types/api/alerts/getAll';
import { Alerts } from 'types/api/alerts/getTriggered';
import { Value } from '../Filter';
import { FilterAlerts } from '../utils';

View File

@ -5,7 +5,7 @@ import AlertStatus from 'container/TriggeredAlerts/TableComponents/AlertStatus';
import convertDateToAmAndPm from 'lib/convertDateToAmAndPm';
import getFormattedDate from 'lib/getFormatedDate';
import React from 'react';
import { Alerts } from 'types/api/alerts/getAll';
import { Alerts } from 'types/api/alerts/getTriggered';
import { Value } from './Filter';
import { FilterAlerts } from './utils';

View File

@ -1,7 +1,7 @@
import getTriggeredApi from 'api/alerts/getTriggered';
import useInterval from 'hooks/useInterval';
import React, { useState } from 'react';
import { Alerts } from 'types/api/alerts/getAll';
import { Alerts } from 'types/api/alerts/getTriggered';
import Filter, { Value } from './Filter';
import FilteredTable from './FilteredTable';

View File

@ -1,4 +1,4 @@
import { Alerts } from 'types/api/alerts/getAll';
import { Alerts } from 'types/api/alerts/getTriggered';
import { Value } from './Filter';

View File

@ -18,6 +18,7 @@ export interface AlertDef {
annotations?: Labels;
evalWindow?: string;
source?: string;
disabled?: boolean;
preferredChannels?: string[];
}

View File

@ -1,7 +1,7 @@
import { Alerts } from './getAll';
import { AlertDef } from './def';
export interface Props {
id: Alerts['id'];
id: AlertDef['id'];
}
export interface PayloadProps {

View File

@ -4,6 +4,13 @@ export interface Props {
id: AlertDef['id'];
}
export interface GettableAlert extends AlertDef {
id: number;
alert: string;
state: string;
disabled: boolean;
}
export type PayloadProps = {
data: AlertDef;
data: GettableAlert;
};

View File

@ -1,32 +1,3 @@
export interface Alerts {
labels: AlertsLabel;
annotations: {
description: string;
summary: string;
[key: string]: string;
};
state: string;
name: string;
id: number;
endsAt: string;
fingerprint: string;
generatorURL: string;
receivers: Receivers[];
startsAt: string;
status: {
inhibitedBy: [];
silencedBy: [];
state: string;
};
updatedAt: string;
}
import { GettableAlert } from './get';
interface Receivers {
name: string;
}
interface AlertsLabel {
[key: string]: string;
}
export type PayloadProps = Alerts[];
export type PayloadProps = GettableAlert[];

View File

@ -1,4 +1,4 @@
import { Alerts } from './getAll';
import { AlertDef } from './def';
export interface Props {
silenced: boolean;
@ -7,8 +7,8 @@ export interface Props {
[key: string]: string | boolean;
}
export interface Group {
alerts: Alerts[];
label: Alerts['labels'];
alerts: AlertDef[];
label: AlertDef['labels'];
receiver: {
[key: string]: string;
};

View File

@ -1,4 +1,33 @@
import { Alerts } from './getAll';
export interface Alerts {
labels: AlertsLabel;
annotations: {
description: string;
summary: string;
[key: string]: string;
};
state: string;
name: string;
id: number;
endsAt: string;
fingerprint: string;
generatorURL: string;
receivers: Receivers[];
startsAt: string;
status: {
inhibitedBy: [];
silencedBy: [];
state: string;
};
updatedAt: string;
}
interface Receivers {
name: string;
}
interface AlertsLabel {
[key: string]: string;
}
export interface Props {
silenced: boolean;

View File

@ -0,0 +1,12 @@
import { GettableAlert } from './get';
export type PayloadProps = GettableAlert;
export interface PatchProps {
disabled?: boolean;
}
export interface Props {
id?: number;
data: PatchProps;
}