From d1b92e99aaa466452e6c2e0f54d7df017d3fcfab Mon Sep 17 00:00:00 2001 From: Xiao Yijun Date: Mon, 11 Sep 2023 10:05:19 +0800 Subject: [PATCH] refactor(console): display webhook test result on details page (#4453) --- packages/console/src/consts/storage.ts | 4 +- .../InlineNotification/index.tsx | 31 +++-- .../TestWebhook/index.module.scss | 4 + .../WebhookSettings/TestWebhook/index.tsx | 112 +++++++++++++++++- .../TestWebhook/use-webhook-test-result.ts | 43 +++++++ packages/core/src/libraries/hook/index.ts | 16 ++- .../src/tests/api/hook/hook.testing.test.ts | 15 ++- .../src/tests/ui/webhooks/helpers.ts | 11 ++ .../index.test.ts} | 40 +++---- .../integration-tests/src/ui-helpers/index.ts | 10 ++ .../phrases/src/locales/de/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/en/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/es/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/fr/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/it/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/ja/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/ko/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/pl-pl/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/pt-br/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/pt-pt/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/ru/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/tr-tr/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/zh-cn/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/zh-hk/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- .../phrases/src/locales/zh-tw/errors/hook.ts | 1 + .../admin-console/webhook-details.ts | 9 +- packages/schemas/src/types/hook.ts | 7 ++ 41 files changed, 381 insertions(+), 62 deletions(-) create mode 100644 packages/console/src/pages/WebhookDetails/WebhookSettings/TestWebhook/use-webhook-test-result.ts create mode 100644 packages/integration-tests/src/tests/ui/webhooks/helpers.ts rename packages/integration-tests/src/tests/ui/{webhooks.test.ts => webhooks/index.test.ts} (83%) diff --git a/packages/console/src/consts/storage.ts b/packages/console/src/consts/storage.ts index d5071c91d..859f41f01 100644 --- a/packages/console/src/consts/storage.ts +++ b/packages/console/src/consts/storage.ts @@ -6,7 +6,8 @@ export type StorageType = | 'appearance_mode' | 'linking_social_connector' | 'checkout_session' - | 'redirect_after_sign_in'; + | 'redirect_after_sign_in' + | 'webhook_test_result'; export const getStorageKey = (forType: T) => `logto:admin_console:${forType}` as const; @@ -17,4 +18,5 @@ export const storageKeys = Object.freeze({ checkoutSession: getStorageKey('checkout_session'), /** The react-router redirect location after sign in. The value should be a stringified Location object. */ redirectAfterSignIn: getStorageKey('redirect_after_sign_in'), + webhookTestResult: getStorageKey('webhook_test_result'), } satisfies Record, string>); diff --git a/packages/console/src/ds-components/InlineNotification/index.tsx b/packages/console/src/ds-components/InlineNotification/index.tsx index 4447ede86..32bd01443 100644 --- a/packages/console/src/ds-components/InlineNotification/index.tsx +++ b/packages/console/src/ds-components/InlineNotification/index.tsx @@ -1,6 +1,7 @@ import type { AdminConsoleKey } from '@logto/phrases'; import classNames from 'classnames'; -import type { ReactNode } from 'react'; +import { forwardRef } from 'react'; +import type { ReactNode, Ref } from 'react'; import Info from '@/assets/icons/info.svg'; import Error from '@/assets/icons/toast-error.svg'; @@ -24,19 +25,23 @@ type Props = { className?: string; }; -function InlineNotification({ - children, - action, - href, - onClick, - severity = 'info', - variant = 'plain', - hasIcon = true, - isActionLoading = false, - className, -}: Props) { +function InlineNotification( + { + children, + action, + href, + onClick, + severity = 'info', + variant = 'plain', + hasIcon = true, + isActionLoading = false, + className, + }: Props, + ref?: Ref +) { return (
(); + const testResultRef = useRef(null); + + const { result, setResult } = useWebhookTestResult(); const [isSendingPayload, setIsSendingPayload] = useState(false); - const api = useApi(); + const api = useApi({ hideErrorToast: true }); const sendTestPayload = async () => { if (isSendingPayload) { @@ -33,13 +42,61 @@ function TestWebhook({ hookId }: Props) { setIsSendingPayload(true); + const endpointUrl = getValues('url'); + const requestTime = Date.now(); + try { const formData = getValues(); const { events, config } = webhookDetailsParser.toRemoteModel(formData); await api.post(`api/hooks/${hookId}/test`, { json: { events, config } }); - toast.success(t('webhook_details.settings.test_payload_sent')); + + setResult({ + result: 'success', + endpointUrl, + message: t('webhook_details.settings.test_result.test_success'), + requestTime, + }); + } catch (error: unknown) { + if (!(error instanceof HTTPError)) { + toast.error(error instanceof Error ? String(error) : t('general.unknown_error')); + return; + } + + const { code, data, message } = await error.response.clone().json(); + + if (code === 'hook.send_test_payload_failed') { + setResult({ + result: 'error', + endpointUrl, + requestTime, + message, + }); + return; + } + + if (code === 'hook.endpoint_responded_with_error') { + const { responseStatus, responseBody } = + trySafe(() => hookTestErrorResponseDataGuard.parse(data)) ?? {}; + + setResult({ + result: 'error', + endpointUrl, + requestTime, + message, + responseStatus, + responseBody, + }); + + return; + } + + throw error; } finally { setIsSendingPayload(false); + // Set timeout to wait for the notification to be rendered + setTimeout(() => { + testResultRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, 50); } }; @@ -59,6 +116,55 @@ function TestWebhook({ hookId }: Props) { }} />
+ {result && ( + { + setResult(undefined); + }} + > +
+ +
+ {result.message && ( +
+ +
+ )} + {result.responseStatus && ( +
+ +
+ )} + {result.responseBody && ( +
+ +
+ )} +
+ +
+
+ )} ); } diff --git a/packages/console/src/pages/WebhookDetails/WebhookSettings/TestWebhook/use-webhook-test-result.ts b/packages/console/src/pages/WebhookDetails/WebhookSettings/TestWebhook/use-webhook-test-result.ts new file mode 100644 index 000000000..30c516ae2 --- /dev/null +++ b/packages/console/src/pages/WebhookDetails/WebhookSettings/TestWebhook/use-webhook-test-result.ts @@ -0,0 +1,43 @@ +import { conditional, conditionalString } from '@silverhand/essentials'; +import { useState } from 'react'; +import { z } from 'zod'; + +import { storageKeys } from '@/consts'; +import { safeParseJson } from '@/utils/json'; + +const webhookTestResultGuard = z.object({ + result: z.enum(['success', 'error']), + endpointUrl: z.string(), + requestTime: z.number(), + message: z.string().optional(), + responseStatus: z.number().optional(), + responseBody: z.string().optional(), +}); + +type WebhookTestResult = z.infer; + +const readResult = () => { + const parsedJson = safeParseJson( + conditionalString(sessionStorage.getItem(storageKeys.webhookTestResult)) + ); + + return conditional(parsedJson.success && webhookTestResultGuard.parse(parsedJson.data)); +}; + +const useWebhookTestResult = () => { + const [testResult, setTestResult] = useState(readResult()); + + return { + result: testResult, + setResult: (result?: WebhookTestResult) => { + setTestResult(result); + if (result) { + sessionStorage.setItem(storageKeys.webhookTestResult, JSON.stringify(result)); + } else { + sessionStorage.removeItem(storageKeys.webhookTestResult); + } + }, + }; +}; + +export default useWebhookTestResult; diff --git a/packages/core/src/libraries/hook/index.ts b/packages/core/src/libraries/hook/index.ts index 873380a88..3caa87065 100644 --- a/packages/core/src/libraries/hook/index.ts +++ b/packages/core/src/libraries/hook/index.ts @@ -5,6 +5,7 @@ import { LogResult, userInfoSelectFields, type HookConfig, + type HookTestErrorResponseData, } from '@logto/schemas'; import { generateStandardId } from '@logto/shared'; import { conditional, pick, trySafe } from '@silverhand/essentials'; @@ -141,12 +142,17 @@ export const createHookLibrary = (queries: Queries) => { }) ); } catch (error: unknown) { - /** - * Note: We only care about whether the test payload is sent to the endpoint of the webhook, - * so we don't care about http errors returned by the endpoint. - */ if (error instanceof HTTPError) { - return; + throw new RequestError( + { + status: 422, + code: 'hook.endpoint_responded_with_error', + }, + { + responseStatus: error.response.statusCode, + responseBody: String(error.response.rawBody), + } satisfies HookTestErrorResponseData + ); } throw new RequestError({ diff --git a/packages/integration-tests/src/tests/api/hook/hook.testing.test.ts b/packages/integration-tests/src/tests/api/hook/hook.testing.test.ts index 10ebf9bf7..889e5f710 100644 --- a/packages/integration-tests/src/tests/api/hook/hook.testing.test.ts +++ b/packages/integration-tests/src/tests/api/hook/hook.testing.test.ts @@ -73,13 +73,18 @@ describe('hook testing', () => { await authedAdminApi.delete(`hooks/${created.id}`); }); - it('should return 204 if the hook endpoint return 500', async () => { + it('should return 422 and contains endpoint response if the hook endpoint return 500', async () => { const payload = getHookCreationPayload(HookEvent.PostRegister); const created = await authedAdminApi.post('hooks', { json: payload }).json(); - const response = await authedAdminApi.post(`hooks/${created.id}/test`, { - json: { events: [HookEvent.PostSignIn], config: { url: responseErrorEndpoint } }, - }); - expect(response.statusCode).toBe(204); + await expectRejects( + authedAdminApi.post(`hooks/${created.id}/test`, { + json: { events: [HookEvent.PostSignIn], config: { url: responseErrorEndpoint } }, + }), + { + code: 'hook.endpoint_responded_with_error', + statusCode: 422, + } + ); // Clean Up await authedAdminApi.delete(`hooks/${created.id}`); diff --git a/packages/integration-tests/src/tests/ui/webhooks/helpers.ts b/packages/integration-tests/src/tests/ui/webhooks/helpers.ts new file mode 100644 index 000000000..80fb3a4b1 --- /dev/null +++ b/packages/integration-tests/src/tests/ui/webhooks/helpers.ts @@ -0,0 +1,11 @@ +import { type Page } from 'puppeteer'; + +export const expectToCreateWebhook = async (page: Page) => { + await expect(page).toClick('div[class$=main] div[class$=headline] > button'); + await expect(page).toClick('span[class$=label]', { text: 'Create new account' }); + await expect(page).toClick('span[class$=label]', { text: 'Sign in' }); + await expect(page).toFill('input[name=name]', 'hook_name'); + await expect(page).toFill('input[name=url]', 'https://example.com/webhook'); + await expect(page).toClick('button[type=submit]'); + await page.waitForSelector('div[class$=header] div[class$=metadata] div[class$=title]'); +}; diff --git a/packages/integration-tests/src/tests/ui/webhooks.test.ts b/packages/integration-tests/src/tests/ui/webhooks/index.test.ts similarity index 83% rename from packages/integration-tests/src/tests/ui/webhooks.test.ts rename to packages/integration-tests/src/tests/ui/webhooks/index.test.ts index dc09065e1..e8dba1f7d 100644 --- a/packages/integration-tests/src/tests/ui/webhooks.test.ts +++ b/packages/integration-tests/src/tests/ui/webhooks/index.test.ts @@ -7,20 +7,13 @@ import { expectToClickDetailsPageOption, expectModalWithTitle, expectConfirmModalAndAct, + expectMainPageWithTitle, } from '#src/ui-helpers/index.js'; import { appendPathname, expectNavigation } from '#src/utils.js'; -await page.setViewport({ width: 1280, height: 720 }); +import { expectToCreateWebhook } from './helpers.js'; -const createWebhookFromWebhooksPage = async () => { - await expect(page).toClick('div[class$=main] div[class$=headline] > button'); - await expect(page).toClick('span[class$=label]', { text: 'Create new account' }); - await expect(page).toClick('span[class$=label]', { text: 'Sign in' }); - await expect(page).toFill('input[name=name]', 'hook_name'); - await expect(page).toFill('input[name=url]', 'https://example.com/webhook'); - await expect(page).toClick('button[type=submit]'); - await page.waitForSelector('div[class$=header] div[class$=metadata] div[class$=title]'); -}; +await page.setViewport({ width: 1280, height: 720 }); describe('webhooks', () => { const logtoConsoleUrl = new URL(logtoConsoleUrlString); @@ -32,23 +25,19 @@ describe('webhooks', () => { it('navigates to webhooks page on clicking sidebar menu', async () => { await expectNavigation(page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href)); - await expect(page).toMatchElement( - 'div[class$=main] div[class$=headline] div[class$=titleEllipsis]', - { - text: 'Webhooks', - } - ); + await expectMainPageWithTitle(page, 'Webhooks'); }); it('can create a new webhook', async () => { await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); - await createWebhookFromWebhooksPage(); + await expectToCreateWebhook(page); // Go to webhook details page await expect(page).toMatchElement('div[class$=main] div[class$=metadata] div[class$=title]', { text: 'hook_name', }); + const hookId = await page.$eval( 'div[class$=main] div[class$=metadata] div[class$=row] div:first-of-type', (element) => element.textContent @@ -87,7 +76,7 @@ describe('webhooks', () => { it('can update webhook details', async () => { await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); - await createWebhookFromWebhooksPage(); + await expectToCreateWebhook(page); await expect(page).toFill('input[name=name]', 'hook_name_updated'); await expect(page).toFill('input[name=url]', 'https://example.com/new-webhook'); @@ -98,7 +87,7 @@ describe('webhooks', () => { it('can disable or enable a webhook', async () => { await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); - await createWebhookFromWebhooksPage(); + await expectToCreateWebhook(page); // Disable webhook await expectToClickDetailsPageOption(page, 'Disable webhook'); @@ -127,7 +116,7 @@ describe('webhooks', () => { it('can regenerate signing key for a webhook', async () => { await page.goto(appendPathname('/console/webhooks', logtoConsoleUrl).href); - await createWebhookFromWebhooksPage(); + await expectToCreateWebhook(page); await expect(page).toClick('button[class$=regenerateButton]'); await expectConfirmModalAndAct(page, { @@ -137,4 +126,15 @@ describe('webhooks', () => { await waitForToast(page, { text: 'Signing key has been regenerated.' }); }); + + it('should display an error info if test failed', async () => { + await expect(page).toClick('div[class$=field] button span', { text: 'Send test payload' }); + await expect(page).toMatchElement('div[class*=inlineNotification] div[class$=content] div', { + text: /Endpoint URL:/, + timeout: 3000, + }); + + await expect(page).toClick('div[class*=inlineNotification] button span', { text: 'Got it' }); + await page.waitForSelector('div[class*=inlineNotification]', { hidden: true }); + }); }); diff --git a/packages/integration-tests/src/ui-helpers/index.ts b/packages/integration-tests/src/ui-helpers/index.ts index 5bf657e74..32937d4eb 100644 --- a/packages/integration-tests/src/ui-helpers/index.ts +++ b/packages/integration-tests/src/ui-helpers/index.ts @@ -129,3 +129,13 @@ export const expectToOpenNewPage = async (browser: Browser, url: string) => { await newPage?.close(); }; + +export const expectMainPageWithTitle = async (page: Page, title: string) => { + await expect(page).toMatchElement( + 'div[class$=main] div[class$=headline] div[class$=titleEllipsis]', + { + text: title, + timeout: 2000, + } + ); +}; diff --git a/packages/phrases/src/locales/de/errors/hook.ts b/packages/phrases/src/locales/de/errors/hook.ts index 92ad655d1..5d4f974cd 100644 --- a/packages/phrases/src/locales/de/errors/hook.ts +++ b/packages/phrases/src/locales/de/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'Sie müssen mindestens ein Ereignis angeben.', send_test_payload_failed: 'Fehler beim Senden des Testpayloads: {{message}}', + endpoint_responded_with_error: 'Endpunkt antwortete mit Fehler.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/de/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/de/translation/admin-console/webhook-details.ts index e8d56a9b8..d6ec578e6 100644 --- a/packages/phrases/src/locales/de/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/de/translation/admin-console/webhook-details.ts @@ -46,7 +46,14 @@ const webhook_details = { test_webhook_description: 'Konfigurieren Sie den Webhook und testen Sie ihn mit Beispieldaten für jede ausgewählte Ereigniskategorie, um eine korrekte Empfangs- und Verarbeitungsfunktion zu überprüfen.', send_test_payload: 'Testnutzlast senden', - test_payload_sent: 'Die Nutzlast wurde erfolgreich gesendet.', + test_result: { + endpoint_url: 'Endpunkt-URL: {{url}}', + message: 'Nachricht: {{message}}', + response_status: 'Antwortstatus: {{status, number}}', + response_body: 'Antwortkörper: {{body}}', + request_time: 'Anforderungszeit: {{time}}', + test_success: 'Der Webhook-Test zum Endpunkt war erfolgreich.', + }, }, }; diff --git a/packages/phrases/src/locales/en/errors/hook.ts b/packages/phrases/src/locales/en/errors/hook.ts index 1db36316d..957515fab 100644 --- a/packages/phrases/src/locales/en/errors/hook.ts +++ b/packages/phrases/src/locales/en/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'You need to provide at least one event.', send_test_payload_failed: 'Failed to send test payload: {{message}}', + endpoint_responded_with_error: 'Endpoint responded with error.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/en/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/en/translation/admin-console/webhook-details.ts index b8a94043b..e5d93cf29 100644 --- a/packages/phrases/src/locales/en/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/en/translation/admin-console/webhook-details.ts @@ -45,7 +45,14 @@ const webhook_details = { test_webhook_description: 'Configure the webhook, and test it with payload examples for each selected event to verify correct reception and processing.', send_test_payload: 'Send test payload', - test_payload_sent: 'The payload has been sent successfully.', + test_result: { + endpoint_url: 'Endpoint URL: {{url}}', + message: 'Message: {{message}}', + response_status: 'Response status: {{status, number}}', + response_body: 'Response body: {{body}}', + request_time: 'Request time: {{time}}', + test_success: 'The webhook test to the endpoint was successful.', + }, }, }; diff --git a/packages/phrases/src/locales/es/errors/hook.ts b/packages/phrases/src/locales/es/errors/hook.ts index 5c9b8fbfb..2ead27ef2 100644 --- a/packages/phrases/src/locales/es/errors/hook.ts +++ b/packages/phrases/src/locales/es/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'Necesita proporcionar al menos un evento.', send_test_payload_failed: 'Error al enviar carga útil de prueba: {{message}}', + endpoint_responded_with_error: 'El punto de enlace respondió con un error.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/es/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/es/translation/admin-console/webhook-details.ts index e0ead7807..cb3c8e7f9 100644 --- a/packages/phrases/src/locales/es/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/es/translation/admin-console/webhook-details.ts @@ -46,7 +46,14 @@ const webhook_details = { test_webhook_description: 'Configure el webhook y pruébelo con ejemplos de carga útil para cada evento seleccionado para verificar la recepción y el procesamiento correcto.', send_test_payload: 'Enviar carga útil de prueba', - test_payload_sent: 'La carga útil se ha enviado con éxito.', + test_result: { + endpoint_url: 'URL del punto final: {{url}}', + message: 'Mensaje: {{message}}', + response_status: 'Estado de la respuesta: {{status, number}}', + response_body: 'Cuerpo de la respuesta: {{body}}', + request_time: 'Tiempo de solicitud: {{time}}', + test_success: 'La prueba del webhook en el punto final fue exitosa.', + }, }, }; diff --git a/packages/phrases/src/locales/fr/errors/hook.ts b/packages/phrases/src/locales/fr/errors/hook.ts index dbbb928e1..de67050d1 100644 --- a/packages/phrases/src/locales/fr/errors/hook.ts +++ b/packages/phrases/src/locales/fr/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'Vous devez fournir au moins un événement.', send_test_payload_failed: "Échec de l'envoi de la charge utile de test : {{message}}", + endpoint_responded_with_error: 'Le point de terminaison a répondu avec une erreur.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/fr/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/fr/translation/admin-console/webhook-details.ts index 3d7297782..66f1f4c8c 100644 --- a/packages/phrases/src/locales/fr/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/fr/translation/admin-console/webhook-details.ts @@ -46,7 +46,14 @@ const webhook_details = { test_webhook_description: 'Configurez le webhook, et testez-le avec des exemples de payload pour chaque événement sélectionné pour vérifier la réception et le traitement corrects.', send_test_payload: 'Envoyer une charge utile de test', - test_payload_sent: 'La charge utile a été envoyée avec succès.', + test_result: { + endpoint_url: 'URL du point de terminaison : {{url}}', + message: 'Message : {{message}}', + response_status: 'Statut de la réponse : {{status, number}}', + response_body: 'Corps de la réponse : {{body}}', + request_time: 'Temps de la demande : {{time}}', + test_success: 'Le test du webhook vers le point de terminaison a réussi.', + }, }, }; diff --git a/packages/phrases/src/locales/it/errors/hook.ts b/packages/phrases/src/locales/it/errors/hook.ts index e72561d03..6eef36269 100644 --- a/packages/phrases/src/locales/it/errors/hook.ts +++ b/packages/phrases/src/locales/it/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'È necessario fornire almeno un evento.', send_test_payload_failed: 'Impossibile inviare il payload di prova: {{message}}', + endpoint_responded_with_error: 'Il punto di accesso ha risposto con un errore.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/it/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/it/translation/admin-console/webhook-details.ts index dbaa40d1c..11ca354a3 100644 --- a/packages/phrases/src/locales/it/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/it/translation/admin-console/webhook-details.ts @@ -45,7 +45,14 @@ const webhook_details = { test_webhook_description: 'Configurare il webhook e testarlo con esempi di payload per ciascun evento selezionato al fine di verificare la corretta ricezione e elaborazione.', send_test_payload: 'Invia Payload di Test', - test_payload_sent: 'Il payload è stato inviato con successo.', + test_result: { + endpoint_url: 'URL del punto di arrivo: {{url}}', + message: 'Messaggio: {{message}}', + response_status: 'Stato della risposta: {{status, number}}', + response_body: 'Corpo della risposta: {{body}}', + request_time: 'Tempo di richiesta: {{time}}', + test_success: 'Il test del webhook al punto di arrivo è stato eseguito con successo.', + }, }, }; diff --git a/packages/phrases/src/locales/ja/errors/hook.ts b/packages/phrases/src/locales/ja/errors/hook.ts index 852df4de2..212eb3722 100644 --- a/packages/phrases/src/locales/ja/errors/hook.ts +++ b/packages/phrases/src/locales/ja/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: '少なくとも1つのイベントを提供する必要があります。', send_test_payload_failed: 'テストペイロードの送信に失敗しました:{{message}}', + endpoint_responded_with_error: 'エンドポイントがエラーで応答しました:{{message}}。', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/ja/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/ja/translation/admin-console/webhook-details.ts index 17aa6f86a..81e9621bc 100644 --- a/packages/phrases/src/locales/ja/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/ja/translation/admin-console/webhook-details.ts @@ -45,7 +45,14 @@ const webhook_details = { test_webhook_description: 'Webhookを設定し、各選択したイベントのペイロード例を使用してテストして、正しい受信と処理を確認してください。', send_test_payload: 'テストペイロードを送信する', - test_payload_sent: 'ペイロードが正常に送信されました。', + test_result: { + endpoint_url: 'エンドポイントURL:{{url}}', + message: 'メッセージ:{{message}}', + response_status: 'レスポンスステータス:{{status, number}}', + response_body: 'レスポンスボディ:{{body}}', + request_time: 'リクエスト時間:{{time}}', + test_success: 'エンドポイントへのWebhookテストは成功しました。', + }, }, }; diff --git a/packages/phrases/src/locales/ko/errors/hook.ts b/packages/phrases/src/locales/ko/errors/hook.ts index 1e08f98ab..c690e1258 100644 --- a/packages/phrases/src/locales/ko/errors/hook.ts +++ b/packages/phrases/src/locales/ko/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: '최소한 하나의 이벤트를 제공해야 합니다.', send_test_payload_failed: '테스트 페이로드 보내기 실패: {{message}}', + endpoint_responded_with_error: '엔드포인트가 오류로 응답했습니다.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/ko/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/ko/translation/admin-console/webhook-details.ts index cf90b6516..702f6acf2 100644 --- a/packages/phrases/src/locales/ko/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/ko/translation/admin-console/webhook-details.ts @@ -44,7 +44,14 @@ const webhook_details = { test_webhook_description: '웹훅을 구성하고 각 선택한 이벤트의 페이로드 예제로 검증하여 올바른 수신 및 처리를 확인하세요.', send_test_payload: '테스트 페이로드 보내기', - test_payload_sent: '페이로드가 성공적으로 보내졌습니다.', + test_result: { + endpoint_url: '엔드포인트 URL: {{url}}', + message: '메시지: {{message}}', + response_status: '응답 상태: {{status, number}}', + response_body: '응답 본문: {{body}}', + request_time: '요청 시간: {{time}}', + test_success: '엔드포인트로의 웹훅 테스트가 성공했습니다.', + }, }, }; diff --git a/packages/phrases/src/locales/pl-pl/errors/hook.ts b/packages/phrases/src/locales/pl-pl/errors/hook.ts index 163acb960..c0640598f 100644 --- a/packages/phrases/src/locales/pl-pl/errors/hook.ts +++ b/packages/phrases/src/locales/pl-pl/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'Musisz podać przynajmniej jedno zdarzenie.', send_test_payload_failed: 'Błąd podczas wysyłania testowego ładunku: {{message}}', + endpoint_responded_with_error: 'Punkt końcowy odpowiedział błędem.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/pl-pl/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/pl-pl/translation/admin-console/webhook-details.ts index 87669be77..68b5709ef 100644 --- a/packages/phrases/src/locales/pl-pl/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/pl-pl/translation/admin-console/webhook-details.ts @@ -45,7 +45,14 @@ const webhook_details = { test_webhook_description: 'Skonfiguruj webhooka i przetestuj go przy użyciu przykładów ładunku dla każdego wybranego zdarzenia, aby sprawdzić poprawne odbieranie i przetwarzanie.', send_test_payload: 'Wyślij testowy ładunek', - test_payload_sent: 'Ladunek został pomyślnie wysłany.', + test_result: { + endpoint_url: 'URL punktu końcowego: {{url}}', + message: 'Wiadomość: {{message}}', + response_status: 'Status odpowiedzi: {{status, number}}', + response_body: 'Treść odpowiedzi: {{body}}', + request_time: 'Czas żądania: {{time}}', + test_success: 'Test webhooka do punktu końcowego zakończył się powodzeniem.', + }, }, }; diff --git a/packages/phrases/src/locales/pt-br/errors/hook.ts b/packages/phrases/src/locales/pt-br/errors/hook.ts index 5cfe5f2f0..d6eeb71a1 100644 --- a/packages/phrases/src/locales/pt-br/errors/hook.ts +++ b/packages/phrases/src/locales/pt-br/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'Você precisa fornecer pelo menos um evento.', send_test_payload_failed: 'Falha ao enviar uma carga de teste: {{message}}', + endpoint_responded_with_error: 'O ponto de extremidade respondeu com erro.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/pt-br/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/pt-br/translation/admin-console/webhook-details.ts index 02dfcca91..7a6c364cb 100644 --- a/packages/phrases/src/locales/pt-br/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/pt-br/translation/admin-console/webhook-details.ts @@ -45,7 +45,14 @@ const webhook_details = { test_webhook_description: 'Configure o webhook e teste-o com exemplos de carga para cada evento selecionado para verificação da recepção e processamento corretos.', send_test_payload: 'Enviar carga de teste', - test_payload_sent: 'A carga foi enviada com sucesso.', + test_result: { + endpoint_url: 'URL do ponto de extremidade: {{url}}', + message: 'Mensagem: {{message}}', + response_status: 'Status da resposta: {{status, number}}', + response_body: 'Corpo da resposta: {{body}}', + request_time: 'Tempo da solicitação: {{time}}', + test_success: 'O teste de webhook para o ponto de extremidade foi bem-sucedido.', + }, }, }; diff --git a/packages/phrases/src/locales/pt-pt/errors/hook.ts b/packages/phrases/src/locales/pt-pt/errors/hook.ts index 3276f9b37..fddfd6b68 100644 --- a/packages/phrases/src/locales/pt-pt/errors/hook.ts +++ b/packages/phrases/src/locales/pt-pt/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'Você precisa fornecer pelo menos um evento.', send_test_payload_failed: 'Falha ao enviar carga de teste: {{message}}', + endpoint_responded_with_error: 'O ponto de extremidade respondeu com erro.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/pt-pt/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/pt-pt/translation/admin-console/webhook-details.ts index d3403200f..6423164dc 100644 --- a/packages/phrases/src/locales/pt-pt/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/pt-pt/translation/admin-console/webhook-details.ts @@ -45,7 +45,14 @@ const webhook_details = { test_webhook_description: 'Configure o webhook e teste-o com exemplos de carga útil para cada evento selecionado para verificar a recepção e o processamento corretos.', send_test_payload: 'Enviar carga de teste', - test_payload_sent: 'A carga foi enviada com sucesso.', + test_result: { + endpoint_url: 'URL do ponto de extremidade: {{url}}', + message: 'Mensagem: {{message}}', + response_status: 'Estado da resposta: {{status, number}}', + response_body: 'Corpo da resposta: {{body}}', + request_time: 'Tempo da solicitação: {{time}}', + test_success: 'O teste de webhook para o ponto de extremidade foi bem-sucedido.', + }, }, }; diff --git a/packages/phrases/src/locales/ru/errors/hook.ts b/packages/phrases/src/locales/ru/errors/hook.ts index 4d275f69e..b6ef741f1 100644 --- a/packages/phrases/src/locales/ru/errors/hook.ts +++ b/packages/phrases/src/locales/ru/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'Вы должны предоставить как минимум одно событие.', send_test_payload_failed: 'Не удалось отправить тестовую нагрузку: {{message}}', + endpoint_responded_with_error: 'Конечная точка ответила ошибкой.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/ru/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/ru/translation/admin-console/webhook-details.ts index 00cf1e0ce..44556b572 100644 --- a/packages/phrases/src/locales/ru/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/ru/translation/admin-console/webhook-details.ts @@ -45,7 +45,14 @@ const webhook_details = { test_webhook_description: 'Настройте вебхук и протестируйте его с примерами нагрузки для каждого выбранного события, чтобы проверить правильный прием и обработку.', send_test_payload: 'Отправить тестовую нагрузку', - test_payload_sent: 'Нагрузка успешно отправлена.', + test_result: { + endpoint_url: 'URL конечной точки: {{url}}', + message: 'Сообщение: {{message}}', + response_status: 'Статус ответа: {{status, number}}', + response_body: 'Тело ответа: {{body}}', + request_time: 'Время запроса: {{time}}', + test_success: 'Тест вебхука на конечную точку прошел успешно.', + }, }, }; diff --git a/packages/phrases/src/locales/tr-tr/errors/hook.ts b/packages/phrases/src/locales/tr-tr/errors/hook.ts index 833157f51..de4133058 100644 --- a/packages/phrases/src/locales/tr-tr/errors/hook.ts +++ b/packages/phrases/src/locales/tr-tr/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: 'En az bir etkinlik sağlamanız gerekiyor.', send_test_payload_failed: 'Test yükü gönderilemedi: {{message}}', + endpoint_responded_with_error: 'Nokta yanıt verdi hatayla.', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/tr-tr/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/tr-tr/translation/admin-console/webhook-details.ts index 41acb3e1e..bdbabf508 100644 --- a/packages/phrases/src/locales/tr-tr/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/tr-tr/translation/admin-console/webhook-details.ts @@ -44,7 +44,14 @@ const webhook_details = { test_webhook_description: 'Webhook’u yapılandırın ve her seçilen olay için yük örnekleriyle test ederek doğru alma ve işleme işlemini doğrulayın.', send_test_payload: 'Test yükünü gönder', - test_payload_sent: 'Yük başarıyla gönderildi.', + test_result: { + endpoint_url: 'Son nokta URL: {{url}}', + message: 'Mesaj: {{message}}', + response_status: 'Yanıt durumu: {{status, number}}', + response_body: 'Yanıt gövdesi: {{body}}', + request_time: 'İstek zamanı: {{time}}', + test_success: 'Son noktaya yapılan webhook testi başarılı oldu.', + }, }, }; diff --git a/packages/phrases/src/locales/zh-cn/errors/hook.ts b/packages/phrases/src/locales/zh-cn/errors/hook.ts index e6e1d6f33..45d462c32 100644 --- a/packages/phrases/src/locales/zh-cn/errors/hook.ts +++ b/packages/phrases/src/locales/zh-cn/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: '你需要提供至少一个事件。', send_test_payload_failed: '无法发送测试内容:{{message}}', + endpoint_responded_with_error: '端点返回了错误。', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/zh-cn/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/zh-cn/translation/admin-console/webhook-details.ts index 03617e643..af50c8c65 100644 --- a/packages/phrases/src/locales/zh-cn/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/zh-cn/translation/admin-console/webhook-details.ts @@ -42,7 +42,14 @@ const webhook_details = { test_webhook_description: '配置 Webhook,并使用每个选定事件的负载示例进行测试,以验证正确的接收和处理。', send_test_payload: '发送测试负载', - test_payload_sent: '负载已成功发送。', + test_result: { + endpoint_url: '端点 URL:{{url}}', + message: '消息:{{message}}', + response_status: '响应状态:{{status, number}}', + response_body: '响应主体:{{body}}', + request_time: '请求时间:{{time}}', + test_success: '向端点发起的 webhook 测试成功。', + }, }, }; diff --git a/packages/phrases/src/locales/zh-hk/errors/hook.ts b/packages/phrases/src/locales/zh-hk/errors/hook.ts index 74b6559d5..f734af64d 100644 --- a/packages/phrases/src/locales/zh-hk/errors/hook.ts +++ b/packages/phrases/src/locales/zh-hk/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: '你需要至少提供一個事件。', send_test_payload_failed: '無法發送測試内容:{{message}}', + endpoint_responded_with_error: '端點返回了錯誤。', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/zh-hk/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/zh-hk/translation/admin-console/webhook-details.ts index da39ebce3..0b7232a74 100644 --- a/packages/phrases/src/locales/zh-hk/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/zh-hk/translation/admin-console/webhook-details.ts @@ -42,7 +42,14 @@ const webhook_details = { test_webhook_description: '配置 webhook,並使用每個選定事件的負載示例進行測試,以驗證正確接收和處理。', send_test_payload: '發送測試負載', - test_payload_sent: '負載已成功發送。', + test_result: { + endpoint_url: '端點URL:{{url}}', + message: '消息:{{message}}', + response_status: '響應狀態:{{status, number}}', + response_body: '響應主體:{{body}}', + request_time: '請求時間:{{time}}', + test_success: '向端點發起的 webhook 測試成功。', + }, }, }; diff --git a/packages/phrases/src/locales/zh-tw/errors/hook.ts b/packages/phrases/src/locales/zh-tw/errors/hook.ts index 21015c701..a71639a0d 100644 --- a/packages/phrases/src/locales/zh-tw/errors/hook.ts +++ b/packages/phrases/src/locales/zh-tw/errors/hook.ts @@ -1,6 +1,7 @@ const hook = { missing_events: '你需要至少提供一個事件。', send_test_payload_failed: '無法发送測試内容:{{message}}', + endpoint_responded_with_error: '端點返回了錯誤。', }; export default Object.freeze(hook); diff --git a/packages/phrases/src/locales/zh-tw/translation/admin-console/webhook-details.ts b/packages/phrases/src/locales/zh-tw/translation/admin-console/webhook-details.ts index a920515a1..eb97d3adb 100644 --- a/packages/phrases/src/locales/zh-tw/translation/admin-console/webhook-details.ts +++ b/packages/phrases/src/locales/zh-tw/translation/admin-console/webhook-details.ts @@ -42,7 +42,14 @@ const webhook_details = { test_webhook_description: '配置 Webhook,並使用每個選定事件的有效載荷示例進行測試,以驗證正確的接收和處理。', send_test_payload: '發送測試有效載荷', - test_payload_sent: '有效載荷已成功發送。', + test_result: { + endpoint_url: '端點URL:{{url}}', + message: '訊息:{{message}}', + response_status: '回應狀態:{{status, number}}', + response_body: '回應主體:{{body}}', + request_time: '請求時間:{{time}}', + test_success: '向端點發起的 webhook 測試成功。', + }, }, }; diff --git a/packages/schemas/src/types/hook.ts b/packages/schemas/src/types/hook.ts index 04b28f324..f041f304e 100644 --- a/packages/schemas/src/types/hook.ts +++ b/packages/schemas/src/types/hook.ts @@ -28,3 +28,10 @@ export const hookResponseGuard = Hooks.guard.extend({ }); export type HookResponse = z.infer; + +export const hookTestErrorResponseDataGuard = z.object({ + responseStatus: z.number(), + responseBody: z.string(), +}); + +export type HookTestErrorResponseData = z.infer;