Skip to content

Commit

Permalink
fix(config-ui): missed some code for webhook connection
Browse files Browse the repository at this point in the history
  • Loading branch information
mintsweet committed Oct 25, 2023
1 parent 7032f2d commit 7c8c448
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 191 deletions.
20 changes: 5 additions & 15 deletions config-ui/src/api/plugin/webhook/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
117 changes: 82 additions & 35 deletions config-ui/src/features/connections/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
};

Expand All @@ -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) => {
Expand All @@ -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: {},
Expand All @@ -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);
})
Expand All @@ -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;

Expand All @@ -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);
14 changes: 13 additions & 1 deletion config-ui/src/features/connections/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
};
};
41 changes: 15 additions & 26 deletions config-ui/src/pages/connection/home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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);
Expand Down Expand Up @@ -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">
Expand All @@ -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 && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
});

Expand Down
Loading

0 comments on commit 7c8c448

Please sign in to comment.