Added remaining API calls for webhooks (#17723)

refs https://github.com/TryGhost/Product/issues/3729
This commit is contained in:
Jono M 2023-08-15 13:29:09 +01:00 committed by GitHub
parent c9012d5e6d
commit 50c36a4c17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 152 additions and 24 deletions

View File

@ -1,4 +1,4 @@
import {Meta, createQuery} from '../utils/apiRequests';
import {Meta, createMutation, createQuery} from '../utils/apiRequests';
// Types
@ -52,31 +52,53 @@ export interface IntegrationsResponseType {
const dataType = 'IntegrationsResponseType';
export const integrationsDataType = dataType;
export const useBrowseIntegrations = createQuery<IntegrationsResponseType>({
dataType,
path: '/integrations/',
defaultSearchParams: {include: 'api_keys,webhooks'}
});
// export const useEditIntegration = createMutation<IntegrationsResponseType, Integration>({
// method: 'PUT',
// path: integration => `/integrations/${integration.id}/`,
// body: integration => ({integrations: [integration]}),
// searchParams: () => ({include: 'roles'}),
// updateQueries: {
// dataType,
// update: () => {} // TODO
// }
// });
export const useCreateIntegration = createMutation<IntegrationsResponseType, Partial<Integration>>({
method: 'POST',
path: () => '/integrations/',
body: integration => ({integrations: [integration]}),
searchParams: () => ({include: 'api_keys,webhooks'}),
updateQueries: {
dataType,
update: (newData, currentData) => ({
...(currentData as IntegrationsResponseType),
integrations: (currentData as IntegrationsResponseType).integrations.concat(newData.integrations)
})
}
});
// export const useDeleteIntegration = createMutation<DeleteIntegrationResponse, string>({
// method: 'DELETE',
// path: id => `/integrations/${id}/`,
// updateQueries: {
// dataType,
// update: (_, currentData, id) => ({
// ...(currentData as IntegrationsResponseType),
// integrations: (currentData as IntegrationsResponseType).integrations.filter(user => user.id !== id)
// })
// }
// });
export const useEditIntegration = createMutation<IntegrationsResponseType, Integration>({
method: 'PUT',
path: integration => `/integrations/${integration.id}/`,
body: integration => ({integrations: [integration]}),
searchParams: () => ({include: 'api_keys,webhooks'}),
updateQueries: {
dataType,
update: (newData, currentData) => ({
...(currentData as IntegrationsResponseType),
integrations: (currentData as IntegrationsResponseType).integrations.map((integration) => {
const newIntegration = newData.integrations.find(({id}) => id === integration.id);
return newIntegration || integration;
})
})
}
});
export const useDeleteIntegration = createMutation<unknown, string>({
method: 'DELETE',
path: id => `/integrations/${id}/`,
updateQueries: {
dataType,
update: (_, currentData, id) => ({
...(currentData as IntegrationsResponseType),
integrations: (currentData as IntegrationsResponseType).integrations.filter(user => user.id !== id)
})
}
});

View File

@ -0,0 +1,80 @@
import {IntegrationsResponseType, integrationsDataType} from './integrations';
import {Meta, createMutation} from '../utils/apiRequests';
// Types
export type Webhook = {
id: string;
event: string;
target_url: string;
name: string;
secret: string | null;
api_version: string;
integration_id: string;
last_triggered_at: string | null;
last_triggered_status: string | null;
last_triggered_error: string | null;
created_at: string;
updated_at: string;
}
export interface WebhooksResponseType {
meta?: Meta;
webhooks: Webhook[];
}
// Requests
export const useCreateWebhook = createMutation<WebhooksResponseType, Partial<Webhook>>({
method: 'POST',
path: () => '/webhooks/',
body: webhook => ({webhooks: [webhook]}),
updateQueries: {
dataType: integrationsDataType,
update: (newData, currentData) => ({
...(currentData as IntegrationsResponseType),
integrations: (currentData as IntegrationsResponseType).integrations.map((integration) => {
const webhook = newData.webhooks[0];
if (webhook.integration_id === integration.id) {
return {...integration, webhooks: [...integration.webhooks, webhook]};
}
return integration;
})
})
}
});
export const useEditWebhook = createMutation<WebhooksResponseType, Webhook>({
method: 'PUT',
path: webhook => `/webhooks/${webhook.id}/`,
body: webhook => ({webhooks: [webhook]}),
updateQueries: {
dataType: integrationsDataType,
update: (newData, currentData) => ({
...(currentData as IntegrationsResponseType),
integrations: (currentData as IntegrationsResponseType).integrations.map(integration => ({
...integration,
webhooks: integration.webhooks.map(webhook => (
webhook.id === newData.webhooks[0].id ? newData.webhooks[0] : webhook
))
}))
})
}
});
export const useDeleteWebhook = createMutation<unknown, string>({
method: 'DELETE',
path: id => `/webhooks/${id}/`,
updateQueries: {
dataType: integrationsDataType,
update: (_, currentData, id) => ({
...(currentData as IntegrationsResponseType),
integrations: (currentData as IntegrationsResponseType).integrations.map(integration => ({
...integration,
webhooks: integration.webhooks.filter(webhook => webhook.id !== id)
}))
})
}
});

View File

@ -1,17 +1,20 @@
import Button from '../../../admin-x-ds/global/Button';
import ConfirmationModal from '../../../admin-x-ds/global/modal/ConfirmationModal';
import List from '../../../admin-x-ds/global/List';
import ListItem from '../../../admin-x-ds/global/ListItem';
import NiceModal from '@ebay/nice-modal-react';
import React, {useState} from 'react';
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
import TabView from '../../../admin-x-ds/global/TabView';
import useRouting from '../../../hooks/useRouting';
import {ReactComponent as AmpIcon} from '../../../assets/icons/amp.svg';
import {ReactComponent as FirstPromoterIcon} from '../../../assets/icons/firstpromoter.svg';
import {Integration, useBrowseIntegrations} from '../../../api/integrations';
import {Integration, useBrowseIntegrations, useCreateIntegration, useDeleteIntegration, useEditIntegration} from '../../../api/integrations';
import {ReactComponent as PinturaIcon} from '../../../assets/icons/pintura.svg';
import {ReactComponent as SlackIcon} from '../../../assets/icons/slack.svg';
import {ReactComponent as UnsplashIcon} from '../../../assets/icons/unsplash.svg';
import {ReactComponent as ZapierIcon} from '../../../assets/icons/zapier.svg';
import {useCreateWebhook, useDeleteWebhook, useEditWebhook} from '../../../api/webhooks';
const IntegrationItem: React.FC<{icon?: React.ReactNode, title: string, detail:string, action:() => void}> = ({
icon,
@ -89,10 +92,33 @@ const BuiltInIntegrations: React.FC = () => {
};
const CustomIntegrations: React.FC<{integrations: Integration[]}> = ({integrations}) => {
const {mutateAsync: createIntegration} = useCreateIntegration();
const {mutateAsync: editIntegration} = useEditIntegration();
const {mutateAsync: deleteIntegration} = useDeleteIntegration();
const {mutateAsync: createWebhook} = useCreateWebhook();
const {mutateAsync: editWebhook} = useEditWebhook();
const {mutateAsync: deleteWebhook} = useDeleteWebhook();
return (
<List>
{integrations.map(integration => (
<IntegrationItem action={() => {}} detail={integration.description || 'No description'} title={integration.name} />)
<IntegrationItem action={() => {
NiceModal.show(ConfirmationModal, {
title: 'TEST API actions',
prompt: <>
Webhooks (will not update until you close and reopen this modal)
<pre><code>{JSON.stringify(integration.webhooks)}</code></pre>
<Button label='Create integration' onClick={() => createIntegration({name: 'Test'})} />
<Button label='Update integration' onClick={() => editIntegration({...integration, name: integration.name + '*'})} />
<Button label='Delete integration' onClick={() => deleteIntegration(integration.id)} />
<Button label='Create webhook' onClick={() => createWebhook({integration_id: integration.id, event: 'post.edited', name: 'Test', target_url: 'https://test.com'})} />
<Button label='Update webhook' onClick={() => editWebhook({...integration.webhooks[0], name: integration.webhooks[0].name + '*'})} />
<Button label='Delete webhook' onClick={() => deleteWebhook(integration.webhooks[0].id)} />
</>,
onOk: modal => modal?.remove()
});
}} detail={integration.description || 'No description'} title={integration.name} />)
)}
</List>
);