From 74d7dd6603002b7f3b2411db5c4cf7518bad864a Mon Sep 17 00:00:00 2001 From: Wang Sijie <wangsijie@silverhand.io> Date: Mon, 14 Mar 2022 11:17:04 +0800 Subject: [PATCH] feat(console): wrap ky to handle response error (#370) * feat(console): wrap ky to handle response error * fix: handle error without body --- .../src/pages/ApiResourceDetails/index.tsx | 4 +-- .../components/CreateForm/index.tsx | 4 +-- .../components/CreateForm/index.tsx | 4 +-- .../src/pages/ConnectorDetails/index.tsx | 15 ++++------ packages/console/src/utilities/api.ts | 29 +++++++++++++++++++ packages/phrases/src/locales/en.ts | 3 ++ packages/phrases/src/locales/zh-cn.ts | 3 ++ 7 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 packages/console/src/utilities/api.ts diff --git a/packages/console/src/pages/ApiResourceDetails/index.tsx b/packages/console/src/pages/ApiResourceDetails/index.tsx index ddc035b21..4378f1f81 100644 --- a/packages/console/src/pages/ApiResourceDetails/index.tsx +++ b/packages/console/src/pages/ApiResourceDetails/index.tsx @@ -1,5 +1,4 @@ import { Resource } from '@logto/schemas'; -import ky from 'ky'; import React, { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -15,6 +14,7 @@ import ImagePlaceholder from '@/components/ImagePlaceholder'; import TabNav, { TabNavLink } from '@/components/TabNav'; import TextInput from '@/components/TextInput'; import { RequestError } from '@/swr'; +import api from '@/utilities/api'; import * as styles from './index.module.scss'; @@ -50,7 +50,7 @@ const ApiResourceDetails = () => { setSubmitting(true); try { - const updatedApiResource = await ky + const updatedApiResource = await api .patch(`/api/resources/${data.id}`, { json: formData }) .json<Resource>(); void mutate(updatedApiResource); diff --git a/packages/console/src/pages/ApiResources/components/CreateForm/index.tsx b/packages/console/src/pages/ApiResources/components/CreateForm/index.tsx index fbda8ecfb..991246a13 100644 --- a/packages/console/src/pages/ApiResources/components/CreateForm/index.tsx +++ b/packages/console/src/pages/ApiResources/components/CreateForm/index.tsx @@ -1,5 +1,4 @@ import { Resource } from '@logto/schemas'; -import ky from 'ky'; import React from 'react'; import { useForm } from 'react-hook-form'; @@ -9,6 +8,7 @@ import CardTitle from '@/components/CardTitle'; import FormField from '@/components/FormField'; import TextInput from '@/components/TextInput'; import Close from '@/icons/Close'; +import api from '@/utilities/api'; import * as styles from './index.module.scss'; @@ -26,7 +26,7 @@ const CreateForm = ({ onClose }: Props) => { const onSubmit = handleSubmit(async (data) => { try { - const createdApiResource = await ky.post('/api/resources', { json: data }).json<Resource>(); + const createdApiResource = await api.post('/api/resources', { json: data }).json<Resource>(); onClose?.(createdApiResource); } catch (error: unknown) { console.error(error); diff --git a/packages/console/src/pages/Applications/components/CreateForm/index.tsx b/packages/console/src/pages/Applications/components/CreateForm/index.tsx index 073593a8b..515921fb9 100644 --- a/packages/console/src/pages/Applications/components/CreateForm/index.tsx +++ b/packages/console/src/pages/Applications/components/CreateForm/index.tsx @@ -1,5 +1,4 @@ import { Application, ApplicationType } from '@logto/schemas'; -import ky from 'ky'; import React from 'react'; import { useController, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; @@ -12,6 +11,7 @@ import RadioGroup, { Radio } from '@/components/RadioGroup'; import TextInput from '@/components/TextInput'; import Close from '@/icons/Close'; import { applicationTypeI18nKey } from '@/types/applications'; +import api from '@/utilities/api'; import TypeDescription from '../TypeDescription'; import * as styles from './index.module.scss'; @@ -40,7 +40,7 @@ const CreateForm = ({ onClose }: Props) => { const onSubmit = handleSubmit(async (data) => { try { - const createdApp = await ky.post('/api/applications', { json: data }).json<Application>(); + const createdApp = await api.post('/api/applications', { json: data }).json<Application>(); onClose?.(createdApp); } catch (error: unknown) { diff --git a/packages/console/src/pages/ConnectorDetails/index.tsx b/packages/console/src/pages/ConnectorDetails/index.tsx index c69a6c95c..a1739ead0 100644 --- a/packages/console/src/pages/ConnectorDetails/index.tsx +++ b/packages/console/src/pages/ConnectorDetails/index.tsx @@ -1,5 +1,4 @@ -import { ConnectorDTO, ConnectorType, RequestErrorBody } from '@logto/schemas'; -import ky, { HTTPError } from 'ky'; +import { ConnectorDTO, ConnectorType } from '@logto/schemas'; import React, { useEffect, useState } from 'react'; import { toast } from 'react-hot-toast'; import { useTranslation } from 'react-i18next'; @@ -17,6 +16,7 @@ import TabNav, { TabNavLink } from '@/components/TabNav'; import Close from '@/icons/Close'; import * as drawerStyles from '@/scss/drawer.module.scss'; import { RequestError } from '@/swr'; +import api from '@/utilities/api'; import SenderTester from './components/SenderTester'; import * as styles from './index.module.scss'; @@ -56,18 +56,15 @@ const ConnectorDetails = () => { try { const configJson = JSON.parse(config) as JSON; setIsSubmitLoading(true); - await ky - .patch(`/api/connectors/${connectorId}`, { json: { config: configJson } }) + await api + .patch(`/api/connectors/${connectorId}`, { + json: { config: configJson }, + }) .json<ConnectorDTO>(); toast.success(t('connector_details.save_success')); } catch (error: unknown) { if (error instanceof SyntaxError) { setSaveError(t('connector_details.save_error_json_parse_error')); - } else if (error instanceof HTTPError) { - const { message } = (await error.response.json()) as RequestErrorBody; - setSaveError(message); - } else { - console.error(error); } } diff --git a/packages/console/src/utilities/api.ts b/packages/console/src/utilities/api.ts new file mode 100644 index 000000000..a9605060b --- /dev/null +++ b/packages/console/src/utilities/api.ts @@ -0,0 +1,29 @@ +import { RequestErrorBody } from '@logto/schemas'; +import { t } from 'i18next'; +import ky from 'ky'; +import { toast } from 'react-hot-toast'; + +const toastError = async (response: Response) => { + try { + const data = (await response.json()) as RequestErrorBody; + toast.error(data.message || t('admin_console.errors.unknown_server_error')); + } catch { + toast.error(t('admin_console.errors.unknown_server_error')); + } +}; + +const api = ky.create({ + hooks: { + beforeError: [ + (error) => { + const { response } = error; + + void toastError(response); + + return error; + }, + ], + }, +}); + +export default api; diff --git a/packages/phrases/src/locales/en.ts b/packages/phrases/src/locales/en.ts index 83f839c64..6302d6c1b 100644 --- a/packages/phrases/src/locales/en.ts +++ b/packages/phrases/src/locales/en.ts @@ -25,6 +25,9 @@ const translation = { form: { required: 'Required', }, + errors: { + unknown_server_error: 'Unknown server error occurred.', + }, tab_sections: { overview: 'Overview', resource_management: 'Resource Management', diff --git a/packages/phrases/src/locales/zh-cn.ts b/packages/phrases/src/locales/zh-cn.ts index 846bde195..2dae60e97 100644 --- a/packages/phrases/src/locales/zh-cn.ts +++ b/packages/phrases/src/locales/zh-cn.ts @@ -27,6 +27,9 @@ const translation = { form: { required: '必填', }, + errors: { + unknown_server_error: '服务器发生未知错误。', + }, tab_sections: { overview: '概览', resource_management: '资源管理',