Add inital UI for new webhooks (#1526)
This commit is contained in:
parent
82a9f8a5e7
commit
00ae375ecf
13 changed files with 720 additions and 1 deletions
|
|
@ -0,0 +1,127 @@
|
|||
import { FormItem, FormItemType } from 'components/GForm/GForm.types';
|
||||
|
||||
export const form: { name: string; fields: FormItem[] } = {
|
||||
name: 'OutgoingWebhook2',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: FormItemType.Input,
|
||||
validation: { required: true },
|
||||
},
|
||||
{
|
||||
name: 'trigger_type',
|
||||
label: 'Trigger type',
|
||||
type: FormItemType.Select,
|
||||
extra: {
|
||||
options: [
|
||||
{
|
||||
value: '0',
|
||||
label: 'Escalation step',
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: 'Triggered',
|
||||
},
|
||||
{
|
||||
value: '2',
|
||||
label: 'Acknowledged',
|
||||
},
|
||||
{
|
||||
value: '3',
|
||||
label: 'Resolved',
|
||||
},
|
||||
{
|
||||
value: '4',
|
||||
label: 'Silenced',
|
||||
},
|
||||
{
|
||||
value: '5',
|
||||
label: 'Unsilenced',
|
||||
},
|
||||
{
|
||||
value: '6',
|
||||
label: 'Unresolved',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'http_method',
|
||||
label: 'HTTP method',
|
||||
type: FormItemType.Select,
|
||||
extra: {
|
||||
options: [
|
||||
{
|
||||
value: 'GET',
|
||||
label: 'GET',
|
||||
},
|
||||
{
|
||||
value: 'POST',
|
||||
label: 'POST',
|
||||
},
|
||||
{
|
||||
value: 'PUT',
|
||||
label: 'PUT',
|
||||
},
|
||||
{
|
||||
value: 'DELETE',
|
||||
label: 'DELETE',
|
||||
},
|
||||
{
|
||||
value: 'OPTIONS',
|
||||
label: 'OPTIONS',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
label: 'Webhook URL',
|
||||
type: FormItemType.Input,
|
||||
validation: { required: true },
|
||||
},
|
||||
{
|
||||
name: 'headers',
|
||||
label: 'Webhook Headers',
|
||||
type: FormItemType.TextArea,
|
||||
extra: {
|
||||
rows: 5,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
type: FormItemType.Input,
|
||||
},
|
||||
{
|
||||
name: 'password',
|
||||
type: FormItemType.Input,
|
||||
},
|
||||
{
|
||||
name: 'authorization_header',
|
||||
type: FormItemType.Input,
|
||||
},
|
||||
{
|
||||
name: 'trigger_template',
|
||||
type: FormItemType.TextArea,
|
||||
description: 'Trigger template must be empty or evaluate to true or 1 for webhook to be sent',
|
||||
extra: {
|
||||
rows: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'data',
|
||||
getDisabled: (form_data) => Boolean(form_data?.forward_whole_payload),
|
||||
type: FormItemType.TextArea,
|
||||
description: 'Available variables: {{ alert_payload }}, {{ alert_group_id }}',
|
||||
extra: {
|
||||
rows: 9,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'forward_all',
|
||||
normalize: (value) => Boolean(value),
|
||||
type: FormItemType.Switch,
|
||||
description: "Forwards whole payload of the alert to the webhook's url as POST data",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
.root {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 16px 0 0 16px;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 4px;
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import React, { useCallback } from 'react';
|
||||
|
||||
import { Button, Drawer } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import GForm from 'components/GForm/GForm';
|
||||
import Text from 'components/Text/Text';
|
||||
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
|
||||
import { OutgoingWebhook2 } from 'models/outgoing_webhook_2/outgoing_webhook_2.types';
|
||||
import { useStore } from 'state/useStore';
|
||||
import { UserActions } from 'utils/authorization';
|
||||
|
||||
import { form } from './OutgoingWebhook2Form.config';
|
||||
|
||||
import styles from 'containers/OutgoingWebhook2Form/OutgoingWebhook2Form.module.css';
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
||||
interface OutgoingWebhook2FormProps {
|
||||
id: OutgoingWebhook2['id'] | 'new';
|
||||
onHide: () => void;
|
||||
onUpdate: () => void;
|
||||
}
|
||||
|
||||
const OutgoingWebhook2Form = observer((props: OutgoingWebhook2FormProps) => {
|
||||
const { id, onUpdate, onHide } = props;
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const { outgoingWebhook2Store } = store;
|
||||
|
||||
const data = id === 'new' ? {} : outgoingWebhook2Store.items[id];
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(data: Partial<OutgoingWebhook2>) => {
|
||||
(id === 'new' ? outgoingWebhook2Store.create(data) : outgoingWebhook2Store.update(id, data)).then(() => {
|
||||
onHide();
|
||||
|
||||
onUpdate();
|
||||
});
|
||||
},
|
||||
[id]
|
||||
);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
scrollableContent
|
||||
title={
|
||||
<Text.Title className={cx('title')} level={4}>
|
||||
{id === 'new' ? 'Create' : 'Edit'} Outgoing Webhook
|
||||
</Text.Title>
|
||||
}
|
||||
onClose={onHide}
|
||||
closeOnMaskClick
|
||||
>
|
||||
<div className={cx('content')} data-testid="test__outgoingWebhook2EditForm">
|
||||
<GForm form={form} data={data} onSubmit={handleSubmit} />
|
||||
<WithPermissionControlTooltip userAction={UserActions.OutgoingWebhooksWrite}>
|
||||
<Button form={form.name} type="submit">
|
||||
{id === 'new' ? 'Create' : 'Update'} Webhook
|
||||
</Button>
|
||||
</WithPermissionControlTooltip>
|
||||
</div>
|
||||
</Drawer>
|
||||
);
|
||||
});
|
||||
|
||||
export default OutgoingWebhook2Form;
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Drawer, Label, VerticalGroup } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import Block from 'components/GBlock/Block';
|
||||
import SourceCode from 'components/SourceCode/SourceCode';
|
||||
import Text from 'components/Text/Text';
|
||||
import { OutgoingWebhook2 } from 'models/outgoing_webhook_2/outgoing_webhook_2.types';
|
||||
import { useStore } from 'state/useStore';
|
||||
|
||||
import styles from 'containers/OutgoingWebhook2Form/OutgoingWebhook2Form.module.css';
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
||||
interface OutgoingWebhook2StatusProps {
|
||||
id: OutgoingWebhook2['id'];
|
||||
onHide: () => void;
|
||||
onUpdate: () => void;
|
||||
}
|
||||
|
||||
function Debug(props) {
|
||||
return (
|
||||
<VerticalGroup spacing="none">
|
||||
<Label>{props.title}</Label>
|
||||
<Block bordered fullWidth>
|
||||
<VerticalGroup spacing="none">
|
||||
{props.source && <SourceCode>{props.source}</SourceCode>}
|
||||
{props.result && props.result !== props.source && (
|
||||
<VerticalGroup spacing="none">
|
||||
<Label>Result</Label>
|
||||
<SourceCode>{props.result}</SourceCode>
|
||||
</VerticalGroup>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
</Block>
|
||||
</VerticalGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const OutgoingWebhook2Status = observer((props: OutgoingWebhook2StatusProps) => {
|
||||
const { id, onHide } = props;
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const { outgoingWebhook2Store } = store;
|
||||
|
||||
const data = outgoingWebhook2Store.items[id];
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
scrollableContent
|
||||
title={
|
||||
<Text.Title className={cx('title')} level={4}>
|
||||
Outgoing Webhook Status
|
||||
</Text.Title>
|
||||
}
|
||||
onClose={onHide}
|
||||
closeOnMaskClick
|
||||
>
|
||||
<div className={cx('content')}>
|
||||
<VerticalGroup>
|
||||
<Label>Webhook Name</Label>
|
||||
<SourceCode>{data.name}</SourceCode>
|
||||
<Label>Trigger Type</Label>
|
||||
<SourceCode>{data.trigger_type_name}</SourceCode>
|
||||
|
||||
{data.last_run ? (
|
||||
<VerticalGroup>
|
||||
<Label>Last Run Time</Label>
|
||||
<SourceCode>{data.last_status_log.last_run_at}</SourceCode>
|
||||
<Label>Input Data</Label>
|
||||
<SourceCode>{JSON.stringify(data.last_status_log.input_data, null, 4)}</SourceCode>
|
||||
|
||||
{data.last_status_log.trigger && (
|
||||
<Debug
|
||||
title="Trigger Template"
|
||||
source={data.trigger_template}
|
||||
result={data.last_status_log.trigger}
|
||||
></Debug>
|
||||
)}
|
||||
{data.last_status_log.url && (
|
||||
<Debug title="URL" source={data.url} result={data.last_status_log.url}></Debug>
|
||||
)}
|
||||
{data.last_status_log.headers && (
|
||||
<Debug title="Headers" source={data.headers} result={data.last_status_log.headers}></Debug>
|
||||
)}
|
||||
{data.last_status_log.data && (
|
||||
<Debug title="Data" source={data.data} result={data.last_status_log.data}></Debug>
|
||||
)}
|
||||
|
||||
{data.last_status_log.response_status && (
|
||||
<VerticalGroup>
|
||||
<Label>Response Code</Label>
|
||||
<SourceCode>{data.last_status_log.response_status}</SourceCode>
|
||||
</VerticalGroup>
|
||||
)}
|
||||
|
||||
{data.last_status_log.response && (
|
||||
<VerticalGroup>
|
||||
<Label>Response Body</Label>
|
||||
<SourceCode>{JSON.stringify(data.last_status_log.response, null, 4)}</SourceCode>
|
||||
</VerticalGroup>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
) : (
|
||||
<Text type="primary" size="medium">
|
||||
An event triggering this webhook has not been sent yet!
|
||||
</Text>
|
||||
)}
|
||||
</VerticalGroup>
|
||||
</div>
|
||||
</Drawer>
|
||||
);
|
||||
});
|
||||
|
||||
export default OutgoingWebhook2Status;
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import { action, observable } from 'mobx';
|
||||
|
||||
import BaseStore from 'models/base_store';
|
||||
import { makeRequest } from 'network';
|
||||
import { RootStore } from 'state';
|
||||
|
||||
import { OutgoingWebhook2 } from './outgoing_webhook_2.types';
|
||||
|
||||
export class OutgoingWebhook2Store extends BaseStore {
|
||||
@observable.shallow
|
||||
items: { [id: string]: OutgoingWebhook2 } = {};
|
||||
|
||||
@observable.shallow
|
||||
searchResult: { [key: string]: Array<OutgoingWebhook2['id']> } = {};
|
||||
|
||||
constructor(rootStore: RootStore) {
|
||||
super(rootStore);
|
||||
|
||||
this.path = '/webhooks/';
|
||||
}
|
||||
|
||||
@action
|
||||
async loadItem(id: OutgoingWebhook2['id'], skipErrorHandling = false): Promise<OutgoingWebhook2> {
|
||||
const outgoingWebhook2 = await this.getById(id, skipErrorHandling);
|
||||
|
||||
this.items = {
|
||||
...this.items,
|
||||
[id]: outgoingWebhook2,
|
||||
};
|
||||
|
||||
return outgoingWebhook2;
|
||||
}
|
||||
|
||||
@action
|
||||
async updateById(id: OutgoingWebhook2['id']) {
|
||||
const response = await this.getById(id);
|
||||
|
||||
this.items = {
|
||||
...this.items,
|
||||
[id]: response,
|
||||
};
|
||||
}
|
||||
|
||||
@action
|
||||
async updateItem(id: OutgoingWebhook2['id'], fromOrganization = false) {
|
||||
const response = await this.getById(id, false, fromOrganization);
|
||||
|
||||
this.items = {
|
||||
...this.items,
|
||||
[id]: response,
|
||||
};
|
||||
}
|
||||
|
||||
@action
|
||||
async updateItems(query = '') {
|
||||
const results = await makeRequest(`${this.path}`, {
|
||||
params: { search: query },
|
||||
});
|
||||
|
||||
this.items = {
|
||||
...this.items,
|
||||
...results.reduce(
|
||||
(acc: { [key: number]: OutgoingWebhook2 }, item: OutgoingWebhook2) => ({
|
||||
...acc,
|
||||
[item.id]: item,
|
||||
}),
|
||||
{}
|
||||
),
|
||||
};
|
||||
|
||||
this.searchResult = {
|
||||
...this.searchResult,
|
||||
[query]: results.map((item: OutgoingWebhook2) => item.id),
|
||||
};
|
||||
}
|
||||
|
||||
getSearchResult(query = '') {
|
||||
if (!this.searchResult[query]) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.searchResult[query].map((outgoingWebhook2Id: OutgoingWebhook2['id']) => this.items[outgoingWebhook2Id]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
export interface OutgoingWebhook2 {
|
||||
authorization_header: string;
|
||||
data: string;
|
||||
forward_all: boolean;
|
||||
http_method: string;
|
||||
id: string;
|
||||
last_run: string;
|
||||
name: string;
|
||||
password: string;
|
||||
team: null;
|
||||
trigger_type: number;
|
||||
trigger_type_name: string;
|
||||
url: string;
|
||||
username: null;
|
||||
headers: string;
|
||||
trigger_template: string;
|
||||
last_status_log?: OutgoingWebhook2Log;
|
||||
}
|
||||
|
||||
export interface OutgoingWebhook2Log {
|
||||
last_run_at: string;
|
||||
input_data: string;
|
||||
url: string;
|
||||
trigger: string;
|
||||
headers: string;
|
||||
data: string;
|
||||
response_status: string;
|
||||
response: string;
|
||||
}
|
||||
|
|
@ -187,6 +187,7 @@ export const ROUTES = {
|
|||
schedules: ['schedules'],
|
||||
schedule: ['schedules/:id'],
|
||||
outgoing_webhooks: ['outgoing_webhooks', 'outgoing_webhooks/:id'],
|
||||
outgoing_webhooks_2: ['outgoing_webhooks_2', 'outgoing_webhooks_2/:id'],
|
||||
maintenance: ['maintenance'],
|
||||
settings: ['settings'],
|
||||
'organization-logs': ['organization-logs'],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -0,0 +1,256 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Button, HorizontalGroup, VerticalGroup } from '@grafana/ui';
|
||||
import cn from 'classnames/bind';
|
||||
import { observer } from 'mobx-react';
|
||||
import LegacyNavHeading from 'navbar/LegacyNavHeading';
|
||||
import { RouteComponentProps, withRouter } from 'react-router-dom';
|
||||
|
||||
import GTable from 'components/GTable/GTable';
|
||||
import PageErrorHandlingWrapper, { PageBaseState } from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper';
|
||||
import {
|
||||
getWrongTeamResponseInfo,
|
||||
initErrorDataState,
|
||||
} from 'components/PageErrorHandlingWrapper/PageErrorHandlingWrapper.helpers';
|
||||
import PluginLink from 'components/PluginLink/PluginLink';
|
||||
import Text from 'components/Text/Text';
|
||||
import WithConfirm from 'components/WithConfirm/WithConfirm';
|
||||
import OutgoingWebhook2Form from 'containers/OutgoingWebhook2Form/OutgoingWebhook2Form';
|
||||
import OutgoingWebhook2Status from 'containers/OutgoingWebhook2Status/OutgoingWebhook2Status';
|
||||
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
|
||||
import { ActionDTO } from 'models/action';
|
||||
import { OutgoingWebhook2 } from 'models/outgoing_webhook_2/outgoing_webhook_2.types';
|
||||
import { makeRequest } from 'network';
|
||||
import { PageProps, WithStoreProps } from 'state/types';
|
||||
import { withMobXProviderContext } from 'state/withStore';
|
||||
import { isUserActionAllowed, UserActions } from 'utils/authorization';
|
||||
import { PLUGIN_ROOT } from 'utils/consts';
|
||||
|
||||
import styles from './OutgoingWebhooks2.module.css';
|
||||
|
||||
const cx = cn.bind(styles);
|
||||
|
||||
interface OutgoingWebhooks2Props extends WithStoreProps, PageProps, RouteComponentProps<{ id: string }> {}
|
||||
|
||||
interface OutgoingWebhooks2State extends PageBaseState {
|
||||
outgoingWebhook2IdToEdit?: OutgoingWebhook2['id'] | 'new';
|
||||
outgoingWebhook2IdToShowStatus?: OutgoingWebhook2['id'];
|
||||
}
|
||||
|
||||
@observer
|
||||
class OutgoingWebhooks2 extends React.Component<OutgoingWebhooks2Props, OutgoingWebhooks2State> {
|
||||
state: OutgoingWebhooks2State = {
|
||||
errorData: initErrorDataState(),
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
this.update().then(this.parseQueryParams);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: OutgoingWebhooks2Props) {
|
||||
if (prevProps.match.params.id !== this.props.match.params.id) {
|
||||
this.parseQueryParams();
|
||||
}
|
||||
}
|
||||
|
||||
parseQueryParams = async () => {
|
||||
this.setState((_prevState) => ({
|
||||
errorData: initErrorDataState(),
|
||||
outgoingWebhook2IdToEdit: undefined,
|
||||
})); // reset state on query parse
|
||||
|
||||
const {
|
||||
store,
|
||||
match: {
|
||||
params: { id },
|
||||
},
|
||||
} = this.props;
|
||||
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let outgoingWebhook2: OutgoingWebhook2 | void = undefined;
|
||||
const isNewWebhook = id === 'new';
|
||||
|
||||
if (!isNewWebhook) {
|
||||
outgoingWebhook2 = await store.outgoingWebhook2Store
|
||||
.loadItem(id, true)
|
||||
.catch((error) => this.setState({ errorData: { ...getWrongTeamResponseInfo(error) } }));
|
||||
}
|
||||
|
||||
if (outgoingWebhook2 || isNewWebhook) {
|
||||
this.setState({ outgoingWebhook2IdToEdit: id });
|
||||
}
|
||||
};
|
||||
|
||||
update = () => {
|
||||
const { store } = this.props;
|
||||
|
||||
return store.outgoingWebhook2Store.updateItems();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { store, query } = this.props;
|
||||
const { outgoingWebhook2IdToEdit, outgoingWebhook2IdToShowStatus, errorData } = this.state;
|
||||
|
||||
const webhooks = store.outgoingWebhook2Store.getSearchResult();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
width: '25%',
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
width: '5%',
|
||||
title: 'Trigger type',
|
||||
dataIndex: 'trigger_type_name',
|
||||
},
|
||||
{
|
||||
width: '5%',
|
||||
title: 'HTTP method',
|
||||
dataIndex: 'http_method',
|
||||
},
|
||||
{
|
||||
width: '35%',
|
||||
title: 'URL',
|
||||
dataIndex: 'url',
|
||||
},
|
||||
{
|
||||
width: '10%',
|
||||
title: 'Last run',
|
||||
dataIndex: 'last_run',
|
||||
},
|
||||
{
|
||||
width: '20%',
|
||||
key: 'action',
|
||||
render: this.renderActionButtons,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<PageErrorHandlingWrapper
|
||||
errorData={errorData}
|
||||
objectName="outgoing webhook 2"
|
||||
pageName="outgoing_webhooks_2"
|
||||
itemNotFoundMessage={`Outgoing webhook with id=${query?.id} is not found. Please select outgoing webhook from the list.`}
|
||||
>
|
||||
{() => (
|
||||
<>
|
||||
<div className={cx('root')}>
|
||||
<GTable
|
||||
emptyText={webhooks ? 'No outgoing webhooks found' : 'Loading...'}
|
||||
title={() => (
|
||||
<div className={cx('header')}>
|
||||
<LegacyNavHeading>
|
||||
<VerticalGroup>
|
||||
<Text.Title level={3}>Outgoing Webhooks 2</Text.Title>
|
||||
<Text type="secondary">
|
||||
⚠️ Preview Functionality! Things will change and things will break! Do not use for critical
|
||||
production processes!
|
||||
</Text>
|
||||
</VerticalGroup>
|
||||
</LegacyNavHeading>
|
||||
<div className="u-pull-right">
|
||||
<PluginLink
|
||||
query={{ page: 'outgoing_webhooks_2', id: 'new' }}
|
||||
disabled={!isUserActionAllowed(UserActions.OutgoingWebhooksWrite)}
|
||||
>
|
||||
<WithPermissionControlTooltip userAction={UserActions.OutgoingWebhooksWrite}>
|
||||
<Button variant="primary" icon="plus">
|
||||
Create
|
||||
</Button>
|
||||
</WithPermissionControlTooltip>
|
||||
</PluginLink>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
data={webhooks}
|
||||
/>
|
||||
</div>
|
||||
{outgoingWebhook2IdToEdit && !outgoingWebhook2IdToShowStatus && (
|
||||
<OutgoingWebhook2Form
|
||||
id={outgoingWebhook2IdToEdit}
|
||||
onUpdate={this.update}
|
||||
onHide={this.handleOutgoingWebhookFormHide}
|
||||
/>
|
||||
)}
|
||||
{outgoingWebhook2IdToShowStatus && (
|
||||
<OutgoingWebhook2Status
|
||||
id={outgoingWebhook2IdToShowStatus}
|
||||
onUpdate={this.update}
|
||||
onHide={this.handleOutgoingWebhookFormHide}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</PageErrorHandlingWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
renderActionButtons = (record: ActionDTO) => {
|
||||
return (
|
||||
<HorizontalGroup justify="flex-end">
|
||||
<WithPermissionControlTooltip key={'status_action'} userAction={UserActions.OutgoingWebhooksRead}>
|
||||
<Button onClick={this.getStatusClickHandler(record.id)} fill="text">
|
||||
Status
|
||||
</Button>
|
||||
</WithPermissionControlTooltip>
|
||||
<WithPermissionControlTooltip key={'edit_action'} userAction={UserActions.OutgoingWebhooksWrite}>
|
||||
<Button onClick={this.getEditClickHandler(record.id)} fill="text">
|
||||
Edit
|
||||
</Button>
|
||||
</WithPermissionControlTooltip>
|
||||
<WithPermissionControlTooltip key={'delete_action'} userAction={UserActions.OutgoingWebhooksWrite}>
|
||||
<WithConfirm>
|
||||
<Button onClick={this.getDeleteClickHandler(record.id)} fill="text" variant="destructive">
|
||||
Delete
|
||||
</Button>
|
||||
</WithConfirm>
|
||||
</WithPermissionControlTooltip>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
};
|
||||
|
||||
getDeleteClickHandler = (id: OutgoingWebhook2['id']) => {
|
||||
return () => {
|
||||
makeRequest(`/webhooks/${id}/`, {
|
||||
method: 'DELETE',
|
||||
withCredentials: true,
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
getEditClickHandler = (id: OutgoingWebhook2['id']) => {
|
||||
const { history } = this.props;
|
||||
|
||||
return () => {
|
||||
this.setState({ outgoingWebhook2IdToEdit: id, outgoingWebhook2IdToShowStatus: undefined });
|
||||
|
||||
history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/${id}`);
|
||||
};
|
||||
};
|
||||
|
||||
handleOutgoingWebhookFormHide = () => {
|
||||
const { history } = this.props;
|
||||
this.setState({ outgoingWebhook2IdToEdit: undefined, outgoingWebhook2IdToShowStatus: undefined });
|
||||
|
||||
history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2`);
|
||||
};
|
||||
|
||||
getStatusClickHandler = (id: OutgoingWebhook2['id']) => {
|
||||
return () => {
|
||||
const { history } = this.props;
|
||||
this.setState({ outgoingWebhook2IdToEdit: undefined, outgoingWebhook2IdToShowStatus: id });
|
||||
|
||||
history.push(`${PLUGIN_ROOT}/outgoing_webhooks_2/${id}`);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export { OutgoingWebhooks2 };
|
||||
|
||||
export default withRouter(withMobXProviderContext(OutgoingWebhooks2));
|
||||
|
|
@ -5,6 +5,7 @@ import IntegrationsPage from 'pages/integrations/Integrations';
|
|||
import MaintenancePage from 'pages/maintenance/Maintenance';
|
||||
import OrganizationLogPage from 'pages/organization-logs/OrganizationLog';
|
||||
import OutgoingWebhooks from 'pages/outgoing_webhooks/OutgoingWebhooks';
|
||||
import OutgoingWebhooks2 from 'pages/outgoing_webhooks_2/OutgoingWebhooks2';
|
||||
import SchedulePage from 'pages/schedule/Schedule';
|
||||
import SchedulesPage from 'pages/schedules/Schedules';
|
||||
import SettingsPage from 'pages/settings/SettingsPage';
|
||||
|
|
@ -55,6 +56,10 @@ export const routes: { [id: string]: NavRoute } = [
|
|||
component: OutgoingWebhooks,
|
||||
id: 'outgoing_webhooks',
|
||||
},
|
||||
{
|
||||
component: OutgoingWebhooks2,
|
||||
id: 'outgoing_webhooks_2',
|
||||
},
|
||||
{
|
||||
component: MaintenancePage,
|
||||
id: 'maintenance',
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@
|
|||
"action": "grafana-oncall-app.schedules:read",
|
||||
"addToNav": true
|
||||
},
|
||||
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Outgoing Webhooks",
|
||||
|
|
@ -87,6 +86,14 @@
|
|||
"action": "grafana-oncall-app.outgoing-webhooks:read",
|
||||
"addToNav": true
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Outgoing Webhooks 2",
|
||||
"path": "/a/grafana-oncall-app/outgoing_webhooks_2",
|
||||
"role": "Viewer",
|
||||
"action": "grafana-oncall-app.outgoing-webhooks:read",
|
||||
"addToNav": false
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Maintenance",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import Integrations from 'pages/integrations/Integrations';
|
|||
import Maintenance from 'pages/maintenance/Maintenance';
|
||||
import OrganizationLogPage from 'pages/organization-logs/OrganizationLog';
|
||||
import OutgoingWebhooks from 'pages/outgoing_webhooks/OutgoingWebhooks';
|
||||
import OutgoingWebhooks2 from 'pages/outgoing_webhooks_2/OutgoingWebhooks2';
|
||||
import Schedule from 'pages/schedule/Schedule';
|
||||
import Schedules from 'pages/schedules/Schedules';
|
||||
import SettingsPage from 'pages/settings/SettingsPage';
|
||||
|
|
@ -161,6 +162,9 @@ export const Root = observer((props: AppRootProps) => {
|
|||
<Route path={getRoutesForPage('outgoing_webhooks')} exact>
|
||||
<OutgoingWebhooks />
|
||||
</Route>
|
||||
<Route path={getRoutesForPage('outgoing_webhooks_2')} exact>
|
||||
<OutgoingWebhooks2 />
|
||||
</Route>
|
||||
<Route path={getRoutesForPage('maintenance')} exact>
|
||||
<Maintenance query={query} />
|
||||
</Route>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { HeartbeatStore } from 'models/heartbeat/heartbeat';
|
|||
import { MaintenanceStore } from 'models/maintenance/maintenance';
|
||||
import { OrganizationLogStore } from 'models/organization_log/organization_log';
|
||||
import { OutgoingWebhookStore } from 'models/outgoing_webhook/outgoing_webhook';
|
||||
import { OutgoingWebhook2Store } from 'models/outgoing_webhook_2/outgoing_webhook_2';
|
||||
import { ResolutionNotesStore } from 'models/resolution_note/resolution_note';
|
||||
import { ScheduleStore } from 'models/schedule/schedule';
|
||||
import { SlackStore } from 'models/slack/slack';
|
||||
|
|
@ -84,6 +85,8 @@ export class RootBaseStore {
|
|||
grafanaTeamStore: GrafanaTeamStore = new GrafanaTeamStore(this);
|
||||
alertReceiveChannelStore: AlertReceiveChannelStore = new AlertReceiveChannelStore(this);
|
||||
outgoingWebhookStore: OutgoingWebhookStore = new OutgoingWebhookStore(this);
|
||||
|
||||
outgoingWebhook2Store: OutgoingWebhook2Store = new OutgoingWebhook2Store(this);
|
||||
alertReceiveChannelFiltersStore: AlertReceiveChannelFiltersStore = new AlertReceiveChannelFiltersStore(this);
|
||||
escalationChainStore: EscalationChainStore = new EscalationChainStore(this);
|
||||
escalationPolicyStore: EscalationPolicyStore = new EscalationPolicyStore(this);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue