From 9d02e1300c434bf05803644142546ac80376c531 Mon Sep 17 00:00:00 2001 From: Xiao Yijun <xiaoyijun@silverhand.io> Date: Tue, 25 Jul 2023 14:24:56 +0800 Subject: [PATCH] fix(console): avoid reading response error body more than once (#4223) --- .../console/src/cloud/hooks/use-cloud-api.ts | 19 ++++++++++++++----- packages/console/src/utils/subscription.ts | 12 ++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/console/src/cloud/hooks/use-cloud-api.ts b/packages/console/src/cloud/hooks/use-cloud-api.ts index c768fc8c5..56c9b9cf2 100644 --- a/packages/console/src/cloud/hooks/use-cloud-api.ts +++ b/packages/console/src/cloud/hooks/use-cloud-api.ts @@ -1,6 +1,6 @@ import type router from '@logto/cloud/routes'; import { useLogto } from '@logto/react'; -import { conditional } from '@silverhand/essentials'; +import { conditional, trySafe } from '@silverhand/essentials'; import Client, { ResponseError } from '@withtyped/client'; import { useMemo } from 'react'; import { toast } from 'react-hot-toast'; @@ -8,15 +8,24 @@ import { z } from 'zod'; import { cloudApi } from '@/consts'; -export const responseErrorBodyGuard = z.object({ +const responseErrorBodyGuard = z.object({ message: z.string(), }); +export const tryReadResponseErrorBody = async (error: ResponseError) => + trySafe(async () => { + // Clone the response to avoid blocking later usage since the response body can only be read once + const responseBody = await error.response.clone().json(); + return responseErrorBodyGuard.parse(responseBody); + }); + export const toastResponseError = async (error: unknown) => { if (error instanceof ResponseError) { - const parsed = responseErrorBodyGuard.safeParse(await error.response.json()); - toast.error(parsed.success ? parsed.data.message : error.message); - return; + const responseBody = await tryReadResponseErrorBody(error); + if (responseBody) { + toast.error(responseBody.message); + return; + } } toast(error instanceof Error ? error.message : String(error)); diff --git a/packages/console/src/utils/subscription.ts b/packages/console/src/utils/subscription.ts index 7a21841e6..6446b9496 100644 --- a/packages/console/src/utils/subscription.ts +++ b/packages/console/src/utils/subscription.ts @@ -1,7 +1,7 @@ import { ResponseError } from '@withtyped/client'; import dayjs from 'dayjs'; -import { responseErrorBodyGuard } from '@/cloud/hooks/use-cloud-api'; +import { tryReadResponseErrorBody } from '@/cloud/hooks/use-cloud-api'; import { type SubscriptionPlanResponse } from '@/cloud/types/router'; import { communitySupportEnabledMap, @@ -64,11 +64,7 @@ export const isExceededQuotaLimitError = async (error: unknown) => { return false; } - try { - const responseBody = await error.response.json(); - const { message } = responseErrorBodyGuard.parse(responseBody); - return message.includes('Exceeded quota limit'); - } catch { - return false; - } + const { message } = (await tryReadResponseErrorBody(error)) ?? {}; + + return Boolean(message?.includes('Exceeded quota limit')); };