You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by kl...@apache.org on 2023/10/25 05:21:53 UTC
[incubator-devlake] branch main updated: fix(config-ui): missed some code for webhook connection (#6311)
This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new a0821bb94 fix(config-ui): missed some code for webhook connection (#6311)
a0821bb94 is described below
commit a0821bb94e271271fca3c01367679b22b510e1c1
Author: 青湛 <0x...@gmail.com>
AuthorDate: Wed Oct 25 18:21:49 2023 +1300
fix(config-ui): missed some code for webhook connection (#6311)
---
config-ui/src/api/plugin/webhook/index.ts | 20 +---
config-ui/src/features/connections/slice.ts | 117 +++++++++++++++------
config-ui/src/features/connections/utils.ts | 14 ++-
config-ui/src/pages/connection/home/index.tsx | 41 +++-----
.../register/webhook/components/create-dialog.tsx | 14 ++-
.../register/webhook/components/delete-dialog.tsx | 7 +-
.../register/webhook/components/edit-dialog.tsx | 15 +--
.../webhook/components/selector-dialog.tsx | 56 ++++------
.../register/webhook/components/view-dialog.tsx | 95 +++++++----------
.../src/plugins/register/webhook/connection.tsx | 12 +--
config-ui/src/types/index.ts | 2 +
config-ui/src/types/{index.ts => status.ts} | 2 +-
config-ui/src/types/{index.ts => webhook.ts} | 22 +++-
13 files changed, 224 insertions(+), 193 deletions(-)
diff --git a/config-ui/src/api/plugin/webhook/index.ts b/config-ui/src/api/plugin/webhook/index.ts
index 27155d23a..18c5c6621 100644
--- a/config-ui/src/api/plugin/webhook/index.ts
+++ b/config-ui/src/api/plugin/webhook/index.ts
@@ -18,23 +18,13 @@
import * as connection from '../../connection';
-export const list = () => connection.list('webhook');
+import { IApiWebhook } from '@/types';
-export const get = (
- id: ID,
-): Promise<{
- name: string;
- postIssuesEndpoint: string;
- closeIssuesEndpoint: string;
- postPipelineDeployTaskEndpoint: string;
- apiKey: {
- id: string;
- apiKey: string;
- };
-}> => connection.get('webhook', id) as any;
+export const list = (): Promise<IApiWebhook[]> => connection.list('webhook') as any;
-export const create = (payload: any): Promise<{ id: string; apiKey: { apiKey: string } }> =>
- connection.create('webhook', payload) as any;
+export const get = (id: ID): Promise<IApiWebhook> => connection.get('webhook', id) as any;
+
+export const create = (payload: any): Promise<IApiWebhook> => connection.create('webhook', payload) as any;
export const remove = (id: ID) => connection.remove('webhook', id);
diff --git a/config-ui/src/features/connections/slice.ts b/config-ui/src/features/connections/slice.ts
index 935379bff..60567509a 100644
--- a/config-ui/src/features/connections/slice.ts
+++ b/config-ui/src/features/connections/slice.ts
@@ -22,15 +22,17 @@ import { flatten } from 'lodash';
import API from '@/api';
import { RootState } from '@/app/store';
import { PluginConfig } from '@/plugins';
-import { IConnection, IConnectionStatus } from '@/types';
+import { IConnection, IConnectionStatus, IWebhook, IStatus } from '@/types';
-import { transformConnection } from './utils';
+import { transformConnection, transformWebhook } from './utils';
const initialState: {
connections: IConnection[];
- status: 'idle' | 'loading' | 'success' | 'failed';
+ webhooks: IWebhook[];
+ status: IStatus;
} = {
connections: [],
+ webhooks: [],
status: 'idle',
};
@@ -43,23 +45,47 @@ export const init = createAsyncThunk('connections/init', async () => {
}
};
- const res = await Promise.all(
- PluginConfig.map(async ({ plugin }) => {
+ const connections = await Promise.all(
+ PluginConfig.filter((p) => p.plugin !== 'webhook').map(async ({ plugin }) => {
const connections = await getConnections(plugin);
return connections.map((connection) => transformConnection(plugin, connection));
}),
);
- return flatten(res);
-});
-export const fetchConnections = createAsyncThunk('connections/fetchConnections', async (plugin: string) => {
- const connections = await API.connection.list(plugin);
+ const webhooks = await Promise.all(
+ PluginConfig.filter((p) => p.plugin === 'webhook').map(async () => {
+ const webhooks = await API.plugin.webhook.list();
+ return webhooks.map((webhook) => transformWebhook(webhook));
+ }),
+ );
+
return {
- plugin,
- connections: connections.map((connection) => transformConnection(plugin, connection)),
+ connections: flatten(connections),
+ webhooks: flatten(webhooks),
};
});
+export const addConnection = createAsyncThunk('connections/addConnection', async ({ plugin, ...payload }: any) => {
+ const connection = await API.connection.create(plugin, payload);
+ return transformConnection(plugin, connection);
+});
+
+export const updateConnection = createAsyncThunk(
+ 'connections/updateConnection',
+ async ({ plugin, connectionId, ...payload }: any) => {
+ const connection = await API.connection.update(plugin, connectionId, payload);
+ return transformConnection(plugin, connection);
+ },
+);
+
+export const removeConnection = createAsyncThunk(
+ 'connections/removeConnection',
+ async ({ plugin, connectionId }: any) => {
+ await API.connection.remove(plugin, connectionId);
+ return `${plugin}-${connectionId}`;
+ },
+);
+
export const testConnection = createAsyncThunk(
'connections/testConnection',
async ({ unique, plugin, endpoint, proxy, token, username, password, authMethod, secretKey, appId }: IConnection) => {
@@ -81,28 +107,31 @@ export const testConnection = createAsyncThunk(
},
);
-export const addConnection = createAsyncThunk('connections/addConnection', async ({ plugin, ...payload }: any) => {
- const connection = await API.connection.create(plugin, payload);
- return transformConnection(plugin, connection);
+export const addWebhook = createAsyncThunk('connections/addWebhook', async (payload: any) => {
+ const webhook = await API.plugin.webhook.create(payload);
+ return transformWebhook(webhook);
});
-export const updateConnection = createAsyncThunk(
- 'connections/updateConnection',
- async ({ plugin, connectionId, ...payload }: any) => {
- const connection = await API.connection.update(plugin, connectionId, payload);
- return transformConnection(plugin, connection);
- },
-);
+export const removeWebhook = createAsyncThunk('connections/removeWebhook', async (id: ID) => {
+ await API.plugin.webhook.remove(id);
+ return id;
+});
-export const removeConnection = createAsyncThunk(
- 'connections/removeConnection',
- async ({ plugin, connectionId }: any) => {
- await API.connection.remove(plugin, connectionId);
- return `${plugin}-${connectionId}`;
- },
-);
+export const updateWebhook = createAsyncThunk('connections/updateWebhook', async ({ id, ...payload }: any) => {
+ const webhook = await API.plugin.webhook.update(id, payload);
+ return webhook;
+});
-export const slice = createSlice({
+export const renewWebhookApiKey = createAsyncThunk('connections/renewWebhookApiKey', async (id: ID, { getState }) => {
+ const webhook = (getState() as RootState).connections.webhooks.find((wh) => wh.id === id) as IWebhook;
+ const apiKey = await API.apiKey.renew(webhook.apiKeyId);
+ return {
+ id: webhook.id,
+ apiKey: apiKey.apiKey,
+ };
+});
+
+export const ConnectionsSlice = createSlice({
name: 'connections',
initialState,
reducers: {},
@@ -112,12 +141,10 @@ export const slice = createSlice({
state.status = 'loading';
})
.addCase(init.fulfilled, (state, action) => {
- state.connections = action.payload;
+ state.connections = action.payload.connections;
+ state.webhooks = action.payload.webhooks;
state.status = 'success';
})
- .addCase(fetchConnections.fulfilled, (state, action) => {
- state.connections = state.connections.concat(action.payload.connections);
- })
.addCase(addConnection.fulfilled, (state, action) => {
state.connections.push(action.payload);
})
@@ -143,13 +170,29 @@ export const slice = createSlice({
if (existingConnection) {
existingConnection.status = action.payload.status;
}
+ })
+ .addCase(addWebhook.fulfilled, (state, action) => {
+ state.webhooks.push(action.payload);
+ })
+ .addCase(removeWebhook.fulfilled, (state, action) => {
+ state.webhooks = state.webhooks.filter((wh) => wh.id !== action.payload);
+ })
+ .addCase(updateWebhook.fulfilled, (state, action) => {
+ state.webhooks = state.webhooks.map((wh) =>
+ wh.id === action.payload.id ? { ...wh, name: action.payload.name } : wh,
+ );
+ })
+ .addCase(renewWebhookApiKey.fulfilled, (state, action) => {
+ state.webhooks = state.webhooks.map((wh) =>
+ wh.id === action.payload.id ? { ...wh, apiKey: action.payload.apiKey } : wh,
+ );
});
},
});
-export const {} = slice.actions;
+export const {} = ConnectionsSlice.actions;
-export default slice.reducer;
+export default ConnectionsSlice.reducer;
export const selectStatus = (state: RootState) => state.connections.status;
@@ -160,3 +203,7 @@ export const selectConnections = (state: RootState, plugin: string) =>
export const selectConnection = (state: RootState, unique: string) =>
state.connections.connections.find((cs) => cs.unique === unique);
+
+export const selectWebhooks = (state: RootState) => state.connections.webhooks;
+
+export const selectWebhook = (state: RootState, id: ID) => state.connections.webhooks.find((wh) => wh.id === id);
diff --git a/config-ui/src/features/connections/utils.ts b/config-ui/src/features/connections/utils.ts
index 00161a4ca..1de94647d 100644
--- a/config-ui/src/features/connections/utils.ts
+++ b/config-ui/src/features/connections/utils.ts
@@ -20,7 +20,7 @@ import * as T from '@/api/connection/types';
import type { PluginConfigType } from '@/plugins';
import { PluginConfig } from '@/plugins';
-import { IConnection, IConnectionStatus } from '@/types';
+import { IConnection, IConnectionStatus, IApiWebhook, IWebhook } from '@/types';
export const transformConnection = (plugin: string, connection: T.Connection): IConnection => {
const config = PluginConfig.find((p) => p.plugin === plugin) as PluginConfigType;
@@ -43,3 +43,15 @@ export const transformConnection = (plugin: string, connection: T.Connection): I
secretKey: connection.secretKey,
};
};
+
+export const transformWebhook = (connection: IApiWebhook): IWebhook => {
+ return {
+ id: connection.id,
+ name: connection.name,
+ postIssuesEndpoint: connection.postIssuesEndpoint,
+ closeIssuesEndpoint: connection.closeIssuesEndpoint,
+ postPipelineDeployTaskEndpoint: connection.postPipelineDeployTaskEndpoint,
+ apiKey: connection.apiKey.apiKey,
+ apiKeyId: connection.apiKey.id,
+ };
+};
diff --git a/config-ui/src/pages/connection/home/index.tsx b/config-ui/src/pages/connection/home/index.tsx
index 5df3a6ad6..bbe948637 100644
--- a/config-ui/src/pages/connection/home/index.tsx
+++ b/config-ui/src/pages/connection/home/index.tsx
@@ -16,13 +16,13 @@
*
*/
-import { useState, useMemo } from 'react';
+import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Tag, Intent } from '@blueprintjs/core';
import { useAppSelector } from '@/app/hook';
import { Dialog } from '@/components';
-import { selectAllConnections } from '@/features/connections';
+import { selectAllConnections, selectWebhooks } from '@/features/connections';
import { PluginConfig, PluginConfigType, ConnectionList, ConnectionForm } from '@/plugins';
import * as S from './styled';
@@ -32,18 +32,10 @@ export const ConnectionHomePage = () => {
const [pluginConfig, setPluginConfig] = useState<PluginConfigType>();
const connections = useAppSelector(selectAllConnections);
+ const webhooks = useAppSelector(selectWebhooks);
const navigate = useNavigate();
- const plugins = useMemo(
- () =>
- PluginConfig.map((p) => ({
- ...p,
- count: connections.filter((cs) => cs.plugin === p.plugin).length,
- })),
- [connections],
- );
-
const handleShowListDialog = (pluginConfig: PluginConfigType) => {
setType('list');
setPluginConfig(pluginConfig);
@@ -77,20 +69,21 @@ export const ConnectionHomePage = () => {
You can create and manage data connections for the following data sources and use them in your Projects.
</h5>
<ul>
- {plugins
- .filter((p) => p.plugin !== 'webhook')
- .map((p) => (
+ {PluginConfig.filter((p) => p.plugin !== 'webhook').map((p) => {
+ const connectionCount = connections.filter((cs) => cs.plugin === p.plugin).length;
+ return (
<li key={p.plugin} onClick={() => handleShowListDialog(p)}>
<img src={p.icon} alt="" />
<span className="name">{p.name}</span>
- <S.Count>{p.count ? `${p.count} connections` : 'No connection'}</S.Count>
+ <S.Count>{connectionCount ? `${connectionCount} connections` : 'No connection'}</S.Count>
{p.isBeta && (
<Tag intent={Intent.WARNING} round>
beta
</Tag>
)}
</li>
- ))}
+ );
+ })}
</ul>
</div>
<div className="block">
@@ -100,20 +93,16 @@ export const ConnectionHomePage = () => {
DORA metrics, etc.
</h5>
<ul>
- {plugins
- .filter((p) => p.plugin === 'webhook')
- .map((p) => (
+ {PluginConfig.filter((p) => p.plugin === 'webhook').map((p) => {
+ const webhookCount = webhooks.length;
+ return (
<li key={p.plugin} onClick={() => handleShowListDialog(p)}>
<img src={p.icon} alt="" />
<span className="name">{p.name}</span>
- <S.Count>{p.count ? `${p.count} connections` : 'No connection'}</S.Count>
- {p.isBeta && (
- <Tag intent={Intent.WARNING} round>
- beta
- </Tag>
- )}
+ <S.Count>{webhookCount ? `${webhookCount} connections` : 'No connection'}</S.Count>
</li>
- ))}
+ );
+ })}
</ul>
</div>
{type === 'list' && pluginConfig && (
diff --git a/config-ui/src/plugins/register/webhook/components/create-dialog.tsx b/config-ui/src/plugins/register/webhook/components/create-dialog.tsx
index e3be58407..6f45f6abf 100644
--- a/config-ui/src/plugins/register/webhook/components/create-dialog.tsx
+++ b/config-ui/src/plugins/register/webhook/components/create-dialog.tsx
@@ -19,8 +19,9 @@
import { useState, useMemo } from 'react';
import { InputGroup, Icon } from '@blueprintjs/core';
-import API from '@/api';
+import { useAppDispatch } from '@/app/hook';
import { Dialog, FormItem, CopyText, ExternalLink } from '@/components';
+import { addWebhook } from '@/features';
import { operator } from '@/utils';
import * as S from '../styled';
@@ -43,17 +44,20 @@ export const CreateDialog = ({ isOpen, onCancel, onSubmitAfter }: Props) => {
apiKey: '',
});
+ const dispatch = useAppDispatch();
+
const prefix = useMemo(() => `${window.location.origin}/api`, []);
const handleSubmit = async () => {
const [success, res] = await operator(
async () => {
- const { id, apiKey } = await API.plugin.webhook.create({ name });
- const { postIssuesEndpoint, closeIssuesEndpoint, postPipelineDeployTaskEndpoint } =
- await API.plugin.webhook.get(id);
+ const { id, apiKey, postIssuesEndpoint, closeIssuesEndpoint, postPipelineDeployTaskEndpoint } = await dispatch(
+ addWebhook({ name }),
+ ).unwrap();
+
return {
id,
- apiKey: apiKey.apiKey,
+ apiKey,
postIssuesEndpoint,
closeIssuesEndpoint,
postPipelineDeployTaskEndpoint,
diff --git a/config-ui/src/plugins/register/webhook/components/delete-dialog.tsx b/config-ui/src/plugins/register/webhook/components/delete-dialog.tsx
index 806a26d5e..a5f298f89 100644
--- a/config-ui/src/plugins/register/webhook/components/delete-dialog.tsx
+++ b/config-ui/src/plugins/register/webhook/components/delete-dialog.tsx
@@ -18,8 +18,9 @@
import { useState } from 'react';
-import API from '@/api';
+import { useAppDispatch } from '@/app/hook';
import { Dialog, Message } from '@/components';
+import { removeWebhook } from '@/features';
import { operator } from '@/utils';
interface Props {
@@ -31,8 +32,10 @@ interface Props {
export const DeleteDialog = ({ initialId, onCancel, onSubmitAfter }: Props) => {
const [operating, setOperating] = useState(false);
+ const dispatch = useAppDispatch();
+
const handleSubmit = async () => {
- const [success] = await operator(() => API.plugin.webhook.remove(initialId), {
+ const [success] = await operator(() => dispatch(removeWebhook(initialId)), {
setOperating,
});
diff --git a/config-ui/src/plugins/register/webhook/components/edit-dialog.tsx b/config-ui/src/plugins/register/webhook/components/edit-dialog.tsx
index f656cdf7c..39e2ba3db 100644
--- a/config-ui/src/plugins/register/webhook/components/edit-dialog.tsx
+++ b/config-ui/src/plugins/register/webhook/components/edit-dialog.tsx
@@ -19,8 +19,9 @@
import { useState, useEffect } from 'react';
import { InputGroup } from '@blueprintjs/core';
-import API from '@/api';
+import { useAppDispatch, useAppSelector } from '@/app/hook';
import { Dialog, FormItem } from '@/components';
+import { updateWebhook, selectWebhook } from '@/features';
import { operator } from '@/utils';
interface Props {
@@ -32,15 +33,15 @@ export const EditDialog = ({ initialId, onCancel }: Props) => {
const [name, setName] = useState('');
const [operating, setOperating] = useState(false);
+ const dispatch = useAppDispatch();
+ const webhook = useAppSelector((state) => selectWebhook(state, initialId));
+
useEffect(() => {
- (async () => {
- const res = await API.plugin.webhook.get(initialId);
- setName(res.name);
- })();
- }, [initialId]);
+ setName(webhook?.name ?? '');
+ }, [webhook]);
const handleSubmit = async () => {
- const [success] = await operator(() => API.plugin.webhook.update(initialId, { name }), {
+ const [success] = await operator(() => dispatch(updateWebhook({ id: initialId, name })), {
setOperating,
});
diff --git a/config-ui/src/plugins/register/webhook/components/selector-dialog.tsx b/config-ui/src/plugins/register/webhook/components/selector-dialog.tsx
index e3ac67c3b..49f900eee 100644
--- a/config-ui/src/plugins/register/webhook/components/selector-dialog.tsx
+++ b/config-ui/src/plugins/register/webhook/components/selector-dialog.tsx
@@ -16,45 +16,29 @@
*
*/
-import { useState, useEffect } from 'react';
-import type { McsItem } from 'miller-columns-select';
+import { useState } from 'react';
import MillerColumnsSelect from 'miller-columns-select';
-import API from '@/api';
+import { useAppSelector } from '@/app/hook';
import { Dialog, FormItem, Loading } from '@/components';
+import { selectWebhooks } from '@/features';
+import { IWebhook } from '@/types';
-import * as T from '../types';
import * as S from '../styled';
interface Props {
isOpen: boolean;
saving: boolean;
onCancel: () => void;
- onSubmit: (items: T.WebhookItemType[]) => void;
+ onSubmit: (items: IWebhook[]) => void;
}
export const SelectorDialog = ({ isOpen, saving, onCancel, onSubmit }: Props) => {
- const [items, setItems] = useState<McsItem<T.WebhookItemType>[]>([]);
- const [selectedItems, setSelectedItems] = useState<T.WebhookItemType[]>([]);
- const [isLast, setIsLast] = useState(false);
+ const [selectedIds, setSelectedIds] = useState<ID[]>([]);
- const updateItems = (arr: any) =>
- arr.map((it: any) => ({
- parentId: null,
- id: it.id,
- title: it.name,
- name: it.name,
- }));
+ const webhooks = useAppSelector(selectWebhooks);
- useEffect(() => {
- (async () => {
- const res = await API.plugin.webhook.list();
- setItems([...updateItems(res)]);
- setIsLast(true);
- })();
- }, []);
-
- const handleSubmit = () => onSubmit(selectedItems);
+ const handleSubmit = () => onSubmit(webhooks.filter((it) => selectedIds.includes(it.id)));
return (
<Dialog
@@ -65,7 +49,7 @@ export const SelectorDialog = ({ isOpen, saving, onCancel, onSubmit }: Props) =>
}}
okText="Confrim"
okLoading={saving}
- okDisabled={!selectedItems.length}
+ okDisabled={!selectedIds.length}
onCancel={onCancel}
onOk={handleSubmit}
>
@@ -74,20 +58,16 @@ export const SelectorDialog = ({ isOpen, saving, onCancel, onSubmit }: Props) =>
<MillerColumnsSelect
columnCount={1}
columnHeight={160}
- getHasMore={() => !isLast}
+ getHasMore={() => false}
renderLoading={() => <Loading size={20} style={{ padding: '4px 12px' }} />}
- items={items}
- selectedIds={selectedItems.map((it) => it.id)}
- onSelectItemIds={(seletedIds: ID[]) =>
- setSelectedItems(
- items
- .filter((it) => seletedIds.includes(it.id))
- .map((it) => ({
- id: it.id,
- name: it.name,
- })),
- )
- }
+ items={webhooks.map((it) => ({
+ parentId: null,
+ id: it.id,
+ title: it.name,
+ name: it.name,
+ }))}
+ selectedIds={selectedIds}
+ onSelectItemIds={setSelectedIds}
/>
</FormItem>
</S.Wrapper>
diff --git a/config-ui/src/plugins/register/webhook/components/view-dialog.tsx b/config-ui/src/plugins/register/webhook/components/view-dialog.tsx
index 263d0e508..e3c89a36e 100644
--- a/config-ui/src/plugins/register/webhook/components/view-dialog.tsx
+++ b/config-ui/src/plugins/register/webhook/components/view-dialog.tsx
@@ -16,11 +16,13 @@
*
*/
-import { useState, useEffect, useMemo } from 'react';
+import { useState, useMemo } from 'react';
import { Button, Intent } from '@blueprintjs/core';
-import API from '@/api';
+import { useAppDispatch, useAppSelector } from '@/app/hook';
import { Dialog, FormItem, CopyText, ExternalLink, Message } from '@/components';
+import { selectWebhook, renewWebhookApiKey } from '@/features';
+import { IWebhook } from '@/types';
import { operator } from '@/utils';
import * as S from '../styled';
@@ -30,68 +32,49 @@ interface Props {
onCancel: () => void;
}
+const transformURI = (prefix: string, webhook: IWebhook) => {
+ return {
+ postIssuesEndpoint: `curl ${prefix}${webhook.postIssuesEndpoint} -X 'POST' -H 'Authorization: Bearer ${
+ webhook.apiKey ?? '{API_KEY}'
+ }' -d '{
+ "issue_key":"DLK-1234",
+ "title":"a feature from DLK",
+ "type":"INCIDENT",
+ "original_status":"TODO",
+ "status":"TODO",
+ "created_date":"2020-01-01T12:00:00+00:00",
+ "updated_date":"2020-01-01T12:00:00+00:00"
+ }'`,
+ closeIssuesEndpoint: `curl ${prefix}${webhook.closeIssuesEndpoint} -X 'POST' -H 'Authorization: Bearer ${
+ webhook.apiKey ?? '{API_KEY}'
+ }'`,
+ postDeploymentsCurl: `curl ${prefix}${webhook.postPipelineDeployTaskEndpoint} -X 'POST' -H 'Authorization: Bearer ${
+ webhook.apiKey ?? '{API_KEY}'
+ }' -d '{
+ "commit_sha":"the sha of deployment commit",
+ "repo_url":"the repo URL of the deployment commit",
+ "start_time":"Optional, eg. 2020-01-01T12:00:00+00:00"
+ }'`,
+ };
+};
+
export const ViewDialog = ({ initialId, onCancel }: Props) => {
- const [record, setRecord] = useState({
- apiKeyId: '',
- postIssuesEndpoint: '',
- closeIssuesEndpoint: '',
- postDeploymentsCurl: '',
- });
const [isOpen, setIsOpen] = useState(false);
const [operating, setOperating] = useState(false);
- const [apiKey, setApiKey] = useState('');
+ const dispatch = useAppDispatch();
+ const webhook = useAppSelector((state) => selectWebhook(state, initialId)) as IWebhook;
const prefix = useMemo(() => `${window.location.origin}/api`, []);
- useEffect(() => {
- (async () => {
- const res = await API.plugin.webhook.get(initialId);
- setRecord({
- apiKeyId: res.apiKey.id,
- postIssuesEndpoint: ` curl ${prefix}${res.postIssuesEndpoint} -X 'POST' -H 'Authorization: Bearer {API_KEY}' -d '{
- "issue_key":"DLK-1234",
- "title":"a feature from DLK",
- "type":"INCIDENT",
- "original_status":"TODO",
- "status":"TODO",
- "created_date":"2020-01-01T12:00:00+00:00",
- "updated_date":"2020-01-01T12:00:00+00:00"
- }'`,
- closeIssuesEndpoint: `curl ${prefix}${res.closeIssuesEndpoint} -X 'POST' -H 'Authorization: Bearer {API_KEY}'`,
- postDeploymentsCurl: `curl ${prefix}${res.postPipelineDeployTaskEndpoint} -X 'POST' -H 'Authorization: Bearer {API_KEY}' -d '{
- "commit_sha":"the sha of deployment commit",
- "repo_url":"the repo URL of the deployment commit",
- "start_time":"Optional, eg. 2020-01-01T12:00:00+00:00"
- }'`,
- });
- })();
- }, [initialId]);
+ const URI = transformURI(prefix, webhook);
const handleGenerateNewKey = async () => {
- const [success, res] = await operator(() => API.apiKey.renew(record.apiKeyId), {
+ const [success] = await operator(() => dispatch(renewWebhookApiKey(initialId)), {
setOperating,
});
if (success) {
setIsOpen(false);
- setApiKey(res.apiKey);
- setRecord({
- ...record,
- postIssuesEndpoint: ` curl ${record.postIssuesEndpoint} -X 'POST' -H 'Authorization: Bearer ${res.apiKey}' -d '{
- "issue_key":"DLK-1234",
- "title":"a feature from DLK",
- "type":"INCIDENT",
- "status":"TODO",
- "created_date":"2020-01-01T12:00:00+00:00",
- "updated_date":"2020-01-01T12:00:00+00:00"
- }'`,
- closeIssuesEndpoint: `curl ${record.closeIssuesEndpoint} -X 'POST' -H 'Authorization: Bearer ${res.apiKey}'`,
- postDeploymentsCurl: `curl ${record.postDeploymentsCurl} -X 'POST' -H 'Authorization: Bearer ${res.apiKey}' -d '{
- "commit_sha":"the sha of deployment commit",
- "repo_url":"the repo URL of the deployment commit",
- "start_time":"Optional, eg. 2020-01-01T12:00:00+00:00"
- }'`,
- });
}
};
@@ -104,7 +87,7 @@ export const ViewDialog = ({ initialId, onCancel }: Props) => {
</p>
<FormItem label="Incident">
<h5>Post to register/update an incident</h5>
- <CopyText content={record.postIssuesEndpoint} />
+ <CopyText content={URI.postIssuesEndpoint} />
<p>
See the{' '}
<ExternalLink link="https://devlake.apache.org/docs/Plugins/webhook#register-issues---update-or-create-issues">
@@ -113,7 +96,7 @@ export const ViewDialog = ({ initialId, onCancel }: Props) => {
.
</p>
<h5>Post to close a registered incident</h5>
- <CopyText content={record.closeIssuesEndpoint} />
+ <CopyText content={URI.closeIssuesEndpoint} />
<p>
See the{' '}
<ExternalLink link="https://devlake.apache.org/docs/Plugins/webhook#register-issues---close-issues-optional">
@@ -124,7 +107,7 @@ export const ViewDialog = ({ initialId, onCancel }: Props) => {
</FormItem>
<FormItem label="Deployments">
<h5>Post to register a deployment</h5>
- <CopyText content={record.postDeploymentsCurl} />
+ <CopyText content={URI.postDeploymentsCurl} />
<p>
See the{' '}
<ExternalLink link="https://devlake.apache.org/docs/Plugins/webhook#deployment">
@@ -137,12 +120,12 @@ export const ViewDialog = ({ initialId, onCancel }: Props) => {
label="API Key"
subLabel="If you have forgotten your API key, you can revoke the previous key and generate a new one as a replacement."
>
- {!apiKey ? (
+ {!webhook.apiKey ? (
<Button intent={Intent.PRIMARY} text="Revoke and generate a new key" onClick={() => setIsOpen(true)} />
) : (
<>
<S.ApiKey>
- <CopyText content={apiKey} />
+ <CopyText content={webhook.apiKey} />
<span>No Expiration</span>
</S.ApiKey>
<S.Tips>
diff --git a/config-ui/src/plugins/register/webhook/connection.tsx b/config-ui/src/plugins/register/webhook/connection.tsx
index 07e68224c..2a6f1a011 100644
--- a/config-ui/src/plugins/register/webhook/connection.tsx
+++ b/config-ui/src/plugins/register/webhook/connection.tsx
@@ -21,11 +21,11 @@ import { Button, Intent } from '@blueprintjs/core';
import { useAppSelector } from '@/app/hook';
import { Buttons, Table, ColumnType, ExternalLink, IconButton } from '@/components';
-import { selectConnections } from '@/features/connections';
+import { selectWebhooks } from '@/features/connections';
import { DOC_URL } from '@/release';
+import { IWebhook } from '@/types';
import { CreateDialog, ViewDialog, EditDialog, DeleteDialog } from './components';
-import * as T from './types';
import * as S from './styled';
@@ -41,19 +41,19 @@ export const WebHookConnection = ({ filterIds, onCreateAfter, onDeleteAfter }: P
const [type, setType] = useState<Type>();
const [currentID, setCurrentID] = useState<ID>();
- const connections = useAppSelector((state) => selectConnections(state, 'webhook'));
+ const webhooks = useAppSelector(selectWebhooks);
const handleHideDialog = () => {
setType(undefined);
setCurrentID(undefined);
};
- const handleShowDialog = (t: Type, r?: T.WebhookItemType) => {
+ const handleShowDialog = (t: Type, r?: IWebhook) => {
setType(t);
setCurrentID(r?.id);
};
- const columns: ColumnType<T.WebhookItemType> = [
+ const columns: ColumnType<IWebhook> = [
{
title: 'ID',
dataIndex: 'id',
@@ -87,7 +87,7 @@ export const WebHookConnection = ({ filterIds, onCreateAfter, onDeleteAfter }: P
</Buttons>
<Table
columns={columns}
- dataSource={connections.filter((cs) => (filterIds ? filterIds.includes(cs.id) : true))}
+ dataSource={webhooks.filter((cs) => (filterIds ? filterIds.includes(cs.id) : true))}
noData={{
text: (
<>
diff --git a/config-ui/src/types/index.ts b/config-ui/src/types/index.ts
index fcc98feb8..c51a9b914 100644
--- a/config-ui/src/types/index.ts
+++ b/config-ui/src/types/index.ts
@@ -17,3 +17,5 @@
*/
export * from './connection';
+export * from './status';
+export * from './webhook';
diff --git a/config-ui/src/types/index.ts b/config-ui/src/types/status.ts
similarity index 92%
copy from config-ui/src/types/index.ts
copy to config-ui/src/types/status.ts
index fcc98feb8..a363098eb 100644
--- a/config-ui/src/types/index.ts
+++ b/config-ui/src/types/status.ts
@@ -16,4 +16,4 @@
*
*/
-export * from './connection';
+export type IStatus = 'idle' | 'loading' | 'success' | 'failed';
diff --git a/config-ui/src/types/index.ts b/config-ui/src/types/webhook.ts
similarity index 65%
copy from config-ui/src/types/index.ts
copy to config-ui/src/types/webhook.ts
index fcc98feb8..5255f1e6f 100644
--- a/config-ui/src/types/index.ts
+++ b/config-ui/src/types/webhook.ts
@@ -16,4 +16,24 @@
*
*/
-export * from './connection';
+export interface IApiWebhook {
+ id: number;
+ name: string;
+ postIssuesEndpoint: string;
+ closeIssuesEndpoint: string;
+ postPipelineDeployTaskEndpoint: string;
+ apiKey: {
+ id: number;
+ apiKey: string;
+ };
+}
+
+export interface IWebhook {
+ id: number;
+ name: string;
+ postIssuesEndpoint: string;
+ closeIssuesEndpoint: string;
+ postPipelineDeployTaskEndpoint: string;
+ apiKey: string;
+ apiKeyId: number;
+}