Skip to content

Commit 63c132c

Browse files
committed
Outgoing Integration history done
1 parent 9692c45 commit 63c132c

File tree

8 files changed

+300
-17
lines changed

8 files changed

+300
-17
lines changed

client/admin/integrations/IntegrationsRoute.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import NotAuthorizedPage from '../NotAuthorizedPage';
66
import IntegrationsPage from './IntegrationsPage';
77
import NewIntegrationsPage from './new/NewIntegrationsPage';
88
import EditIntegrationsPage from './edit/EditIntegrationsPage';
9+
import OutgoingWebhookHistoryPage from './edit/OutgoingWebhookHistoryPage';
910

1011
function IntegrationsRoute() {
1112
const canViewIntegrationsPage = useAtLeastOnePermission([
@@ -30,6 +31,10 @@ function IntegrationsRoute() {
3031
return <EditIntegrationsPage />;
3132
}
3233

34+
if (context === 'history') {
35+
return <OutgoingWebhookHistoryPage />;
36+
}
37+
3338
return <IntegrationsPage />;
3439
}
3540

client/admin/integrations/edit/EditIncomingWebhook.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { SuccessModal, DeleteWarningModal } from './EditIntegrationsPage';
55
import { useTranslation } from '../../../contexts/TranslationContext';
66
import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../hooks/useEndpointDataExperimental';
77
import { useMethod, useAbsoluteUrl } from '../../../contexts/ServerContext';
8-
import { useHilightedCode } from '../../../hooks/useHilightedCode';
8+
import { useHilightCode } from '../../../hooks/useHilightCode';
99
import { useEndpointAction } from '../../../hooks/useEndpointAction';
1010
import { useRoute } from '../../../contexts/RouterContext';
1111
import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
@@ -32,7 +32,7 @@ export default function EditIncomingWebhookWithData({ integrationId, ...props })
3232
}
3333

3434
if (error) {
35-
return <Box mbs='x16' {...props}>{t('User_not_found')}</Box>;
35+
return <Box mbs='x16' {...props}>{t('Oops_page_not_found')}</Box>;
3636
}
3737

3838
return <EditIncomingWebhook data={data.integration} onChange={onChange} {...props}/>;
@@ -53,6 +53,8 @@ function EditIncomingWebhook({ data, setData, onChange, ...props }) {
5353

5454
const router = useRoute('admin-integrations');
5555

56+
const hilightCode = useHilightCode();
57+
5658
const handleDeleteIntegration = () => {
5759
const closeModal = () => setModal();
5860
const onDelete = async () => {
@@ -104,7 +106,7 @@ function EditIncomingWebhook({ data, setData, onChange, ...props }) {
104106
url,
105107
});
106108

107-
const hilightedExampleJson = useHilightedCode('json', JSON.stringify(exampleData, null, 2));
109+
const hilightedExampleJson = hilightCode('json', JSON.stringify(exampleData, null, 2));
108110

109111
return <>
110112
<Page.ScrollableContent pb='x24' mi='neg-x24' is='form' qa-admin-user-edit='form' { ...props }>

client/admin/integrations/edit/EditIntegrationsPage.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,24 @@ export default function NewIntegrationsPage({ ...props }) {
5252

5353
const router = useRoute('admin-integrations');
5454

55+
const type = useRouteParameter('type');
56+
const integrationId = useRouteParameter('id');
57+
5558
const handleClickReturn = () => {
5659
router.push({ });
5760
};
5861

59-
const type = useRouteParameter('type');
60-
const integrationId = useRouteParameter('id');
62+
const handleClickHistory = () => {
63+
router.push({ context: 'history', type: 'outgoing', id: integrationId });
64+
};
6165

6266
return <Page flexDirection='column' {...props}>
6367
<Page.Header title={t('Integrations')} >
6468
<ButtonGroup>
6569
<Button onClick={handleClickReturn}>
6670
<Icon name='back' size='x16'/> {t('Back')}
6771
</Button>
68-
{/* {type === 'outgoing' && <Button onClick={handleClickHistory}>{t('History')}</Button>} */}
72+
{type === 'outgoing' && <Button onClick={handleClickHistory}>{t('History')}</Button>}
6973
</ButtonGroup>
7074
</Page.Header>
7175
<Page.ScrollableContentWithShadow>

client/admin/integrations/edit/EditOutgoingWebhook.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../hooks/use
2121
import { useEndpointAction } from '../../../hooks/useEndpointAction';
2222
import { useRoute } from '../../../contexts/RouterContext';
2323
import { useMethod } from '../../../contexts/ServerContext';
24-
import { useHilightedCode } from '../../../hooks/useHilightedCode';
24+
import { useHilightCode } from '../../../hooks/useHilightCode';
2525
import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
2626
import { useExampleData } from '../exampleIncomingData';
2727
import { integrations as eventList } from '../../../../app/integrations/lib/rocketchat';
@@ -47,7 +47,7 @@ export default function EditOutgoingWebhookWithData({ integrationId, ...props })
4747
}
4848

4949
if (error) {
50-
return <Box mbs='x16' {...props}>{t('User_not_found')}</Box>;
50+
return <Box mbs='x16' {...props}>{t('Oops_page_not_found')}</Box>;
5151
}
5252

5353
return <EditOutgoingWebhook data={data.integration} onChange={onChange} {...props}/>;
@@ -92,6 +92,8 @@ function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) {
9292

9393
const router = useRoute('admin-integrations');
9494

95+
const hilightCode = useHilightCode();
96+
9597
const deleteQuery = useMemo(() => ({ type: 'webhook-outgoing', integrationId: data._id }), [data._id]);
9698
const deleteIntegration = useEndpointAction('POST', 'integrations.remove', deleteQuery);
9799

@@ -174,7 +176,7 @@ function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) {
174176
url: null,
175177
});
176178

177-
const hilightedExampleJson = useHilightedCode('json', JSON.stringify(exampleData, null, 2));
179+
const hilightedExampleJson = hilightCode('json', JSON.stringify(exampleData, null, 2));
178180

179181
const showChannel = useMemo(() => outgoingEvents[event].use.channel, [event]);
180182
const showTriggerWords = useMemo(() => outgoingEvents[event].use.triggerWords, [event]);
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
import { Button, ButtonGroup, Icon, Headline, Skeleton, Box, Accordion, Field, FieldGroup, Pagination } from '@rocket.chat/fuselage';
2+
import React, { useMemo, useCallback, useState, useEffect } from 'react';
3+
4+
import Page from '../../../components/basic/Page';
5+
import { useTranslation } from '../../../contexts/TranslationContext';
6+
import { useHilightCode } from '../../../hooks/useHilightCode';
7+
import { integrations as eventList } from '../../../../app/integrations/lib/rocketchat';
8+
import { useMethod } from '../../../contexts/ServerContext';
9+
import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../hooks/useEndpointDataExperimental';
10+
import { useRoute, useRouteParameter } from '../../../contexts/RouterContext';
11+
import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime';
12+
import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
13+
14+
function HistoryItem({ data, onChange, ...props }) {
15+
const t = useTranslation();
16+
17+
const hilightCode = useHilightCode();
18+
19+
const replayOutgoingIntegration = useMethod('replayOutgoingIntegration');
20+
21+
const {
22+
_id,
23+
_createdAt,
24+
_updatedAt,
25+
httpResult,
26+
event,
27+
step,
28+
httpCallData,
29+
data: dataSentToTrigger,
30+
prepareSentMessage,
31+
processSentMessage,
32+
url,
33+
httpError,
34+
errorStack,
35+
error,
36+
integration: { _id: integrationId },
37+
} = data;
38+
39+
const handleClickReplay = useCallback((e) => {
40+
e.stopPropagation();
41+
replayOutgoingIntegration({ integrationId, historyId: _id });
42+
onChange();
43+
}, [_id]);
44+
45+
const formatDateAndTime = useFormatDateAndTime();
46+
47+
return <Accordion.Item
48+
title={
49+
<Box display='inline-flex' w='full' flexDirection='row' justifyContent='space-between'>
50+
<Box display='flex' flexDirection='row' alignItems='center'>
51+
<Icon name='info-circled' size='x16' mie='x4'/>{formatDateAndTime(_createdAt)}
52+
</Box>
53+
<Button ghost onClick={handleClickReplay}>{t('Replay')}</Button>
54+
</Box>
55+
}
56+
{...props}
57+
>
58+
<FieldGroup>
59+
<Field>
60+
<Field.Label>{t('Status')}</Field.Label>
61+
<Field.Row>
62+
<Box withRichContent w='full'>
63+
<code>{error ? t('Failure') : t('Success')}</code>
64+
</Box>
65+
</Field.Row>
66+
</Field>
67+
<Field>
68+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Time_Triggered')}</Field.Label>
69+
<Field.Row>
70+
<Box withRichContent w='full'>
71+
<code>{_createdAt}</code>
72+
</Box>
73+
</Field.Row>
74+
</Field>
75+
<Field>
76+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Time_Ended_Or_Error')}</Field.Label>
77+
<Field.Row>
78+
<Box withRichContent w='full'>
79+
<code>{_updatedAt}</code>
80+
</Box>
81+
</Field.Row>
82+
</Field>
83+
<Field>
84+
<Field.Label>{t('Event_Trigger')}</Field.Label>
85+
<Field.Row>
86+
<Box withRichContent w='full'>
87+
<code>{t(eventList.outgoingEvents[event].label)}</code>
88+
</Box>
89+
</Field.Row>
90+
</Field>
91+
<Field>
92+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Trigger_Step')}</Field.Label>
93+
<Field.Row>
94+
<Box withRichContent w='full'>
95+
<code>{step}</code>
96+
</Box>
97+
</Field.Row>
98+
</Field>
99+
<Field>
100+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Data_Passed_To_Trigger')}</Field.Label>
101+
<Field.Row>
102+
<Box withRichContent w='full'>
103+
<pre><code dangerouslySetInnerHTML={{ __html: hilightCode('json', JSON.stringify(dataSentToTrigger, null, 2)) }}></code></pre>
104+
</Box>
105+
</Field.Row>
106+
</Field>
107+
{prepareSentMessage && <Field>
108+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Messages_Sent_From_Prepare_Script')}</Field.Label>
109+
<Field.Row>
110+
<Box withRichContent w='full'>
111+
<pre><code dangerouslySetInnerHTML={{ __html: hilightCode('json', JSON.stringify(prepareSentMessage, null, 2)) }}></code></pre>
112+
</Box>
113+
</Field.Row>
114+
</Field>}
115+
{processSentMessage && <Field>
116+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Messages_Sent_From_Process_Script')}</Field.Label>
117+
<Field.Row>
118+
<Box withRichContent w='full'>
119+
<pre><code dangerouslySetInnerHTML={{ __html: hilightCode('json', JSON.stringify(processSentMessage, null, 2)) }}></code></pre>
120+
</Box>
121+
</Field.Row>
122+
</Field>}
123+
{url && <Field>
124+
<Field.Label>{t('URL')}</Field.Label>
125+
<Field.Row>
126+
<Box withRichContent w='full'>
127+
<code>{url}</code>
128+
</Box>
129+
</Field.Row>
130+
</Field>}
131+
{httpCallData && <Field>
132+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Data_Passed_To_URL')}</Field.Label>
133+
<Field.Row>
134+
<Box withRichContent w='full'>
135+
<pre><code dangerouslySetInnerHTML={{ __html: hilightCode('json', JSON.stringify(httpCallData, null, 2)) }}></code></pre>
136+
</Box>
137+
</Field.Row>
138+
</Field>}
139+
{httpError && <Field>
140+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Http_Response_Error')}</Field.Label>
141+
<Field.Row>
142+
<Box withRichContent w='full'>
143+
<pre><code dangerouslySetInnerHTML={{ __html: hilightCode('json', JSON.stringify(httpError, null, 2)) }}></code></pre>
144+
</Box>
145+
</Field.Row>
146+
</Field>}
147+
{httpResult && <Field>
148+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Http_Response')}</Field.Label>
149+
<Field.Row>
150+
<Box withRichContent w='full'>
151+
<pre><code dangerouslySetInnerHTML={{ __html: hilightCode('json', httpResult) }}></code></pre>
152+
</Box>
153+
</Field.Row>
154+
</Field>}
155+
{errorStack && <Field>
156+
<Field.Label>{t('Integration_Outgoing_WebHook_History_Error_Stacktrace')}</Field.Label>
157+
<Field.Row>
158+
<Box withRichContent w='full'>
159+
<pre><code dangerouslySetInnerHTML={{ __html: hilightCode('json', JSON.stringify(errorStack, null, 2)) }}></code></pre>
160+
</Box>
161+
</Field.Row>
162+
</Field>}
163+
</FieldGroup>
164+
</Accordion.Item>;
165+
}
166+
167+
function HistoryContent({ data, state, onChange, ...props }) {
168+
const t = useTranslation();
169+
170+
const [loadedData, setLoadedData] = useState();
171+
useEffect(() => {
172+
if (state === ENDPOINT_STATES.DONE) { setLoadedData(data); }
173+
}, [state]);
174+
175+
if (!loadedData || (!loadedData && state === ENDPOINT_STATES.LOADING)) {
176+
return <Box w='full' pb='x24' {...props}>
177+
<Headline.Skeleton mbe='x4'/>
178+
<Skeleton mbe='x8' />
179+
<Headline.Skeleton mbe='x4'/>
180+
<Skeleton mbe='x8'/>
181+
<Headline.Skeleton mbe='x4'/>
182+
<Skeleton mbe='x8'/>
183+
</Box>;
184+
}
185+
186+
if (loadedData.history.length < 1) {
187+
return <Box mbs='x16' {...props}>{t('Integration_Outgoing_WebHook_No_History')}</Box>;
188+
}
189+
190+
return <>
191+
<Accordion w='full' maxWidth='x600' alignSelf='center' key='content'>
192+
{loadedData.history.map((current) => <HistoryItem
193+
data={current}
194+
key={current._id}
195+
onChange={onChange}
196+
/>)}
197+
</Accordion>
198+
</>;
199+
}
200+
201+
function OutgoingWebhookHistoryPage(props) {
202+
const dispatchToastMessage = useToastMessageDispatch();
203+
const t = useTranslation();
204+
205+
const [cache, setCache] = useState();
206+
const [current, setCurrent] = useState();
207+
const [itemsPerPage, setItemsPerPage] = useState();
208+
const onChange = useCallback(() => {
209+
setCache(new Date());
210+
});
211+
212+
const router = useRoute('admin-integrations');
213+
214+
const clearHistory = useMethod('clearIntegrationHistory');
215+
216+
const handleClearHistory = async () => {
217+
try {
218+
await clearHistory();
219+
dispatchToastMessage({ type: 'success', message: t('Integration_History_Cleared') });
220+
onChange();
221+
} catch (e) {
222+
dispatchToastMessage({ type: 'error', message: e });
223+
}
224+
};
225+
226+
const handleClickReturn = () => {
227+
router.push({ });
228+
};
229+
230+
const id = useRouteParameter('id');
231+
232+
const query = useMemo(() => ({
233+
id,
234+
cout: itemsPerPage,
235+
offset: current,
236+
}), [id, itemsPerPage, current, cache]);
237+
238+
const { data, state } = useEndpointDataExperimental('integrations.history', query);
239+
240+
const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), []);
241+
242+
return <Page flexDirection='column' {...props}>
243+
<Page.Header title={t('Integration_Outgoing_WebHook_History')}>
244+
<ButtonGroup>
245+
<Button onClick={handleClickReturn}>
246+
<Icon name='back' size='x16'/> {t('Back')}
247+
</Button>
248+
<Button primary danger onClick={handleClearHistory} disabled={!(data && data.history.length > 0)}>
249+
<Icon name='trash'/> {t('clear_history')}
250+
</Button>
251+
</ButtonGroup>
252+
</Page.Header>
253+
<Page.ScrollableContentWithShadow>
254+
<HistoryContent key='historyContent' data={data} state={state} onChange={onChange} />
255+
<Pagination
256+
current={current}
257+
itemsPerPage={itemsPerPage}
258+
itemsPerPageLabel={t('Items_per_page:')}
259+
showingResultsLabel={showingResultsLabel}
260+
count={(data && data.total) || 0}
261+
onSetItemsPerPage={setItemsPerPage}
262+
onSetCurrent={setCurrent}
263+
/>
264+
</Page.ScrollableContentWithShadow>
265+
</Page>;
266+
}
267+
268+
export default OutgoingWebhookHistoryPage;

0 commit comments

Comments
 (0)