From 242e458959864159f7c38092d23d48547f4b2bdd Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 29 Aug 2024 16:49:40 +0200 Subject: [PATCH 01/11] fix: pool page error when pool id is not found --- .../pools/[chain]/[variant]/[id]/layout.tsx | 49 +++++++++++++------ lib/shared/components/alerts/BalAlert.tsx | 13 ++++- .../containers/DefaultPageContainer.tsx | 2 +- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx index 6966a3e6e..fe87f34a9 100644 --- a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx +++ b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx @@ -6,10 +6,12 @@ import { PoolDetailSkeleton } from '@/lib/modules/pool/PoolDetail/PoolDetailSkel import { getApolloServerClient } from '@/lib/shared/services/api/apollo-server.client' import { GetPoolDocument } from '@/lib/shared/services/api/generated/graphql' import { Metadata } from 'next' -import { Box } from '@chakra-ui/react' import { PoolProvider } from '@/lib/modules/pool/PoolProvider' import { getProjectConfig } from '@/lib/config/getProjectConfig' import { arrayToSentence } from '@/lib/shared/utils/strings' +import { DefaultPageContainer } from '@/lib/shared/components/containers/DefaultPageContainer' +import { BalAlert } from '@/lib/shared/components/alerts/BalAlert' +import { ensureError } from '@/lib/shared/utils/errors' type Props = PropsWithChildren<{ params: Omit & { chain: ChainSlug } @@ -21,24 +23,28 @@ async function getPoolQuery(chain: ChainSlug, id: string) { const _chain = slugToChainMap[chain] const variables = { id: id.toLowerCase(), chain: _chain } - return await getApolloServerClient().query({ - query: GetPoolDocument, - variables, - context: { - fetchOptions: { - next: { revalidate: 30 }, + try { + const result = await getApolloServerClient().query({ + query: GetPoolDocument, + variables, + context: { + fetchOptions: { + next: { revalidate: 30 }, + }, }, - }, - }) + }) + return { data: result.data, error: null } + } catch (error: unknown) { + return { data: null, error: ensureError(error) } + } } export async function generateMetadata({ params: { id, chain, variant }, }: Props): Promise { - const { - data: { pool }, - } = await getPoolQuery(chain, id) + const { data } = await getPoolQuery(chain, id) + const pool = data?.pool if (!pool) return {} const poolTokenString = arrayToSentence(pool.displayTokens.map(token => token.symbol)) @@ -54,12 +60,25 @@ export async function generateMetadata({ export default async function PoolLayout({ params: { id, chain, variant }, children }: Props) { const _chain = slugToChainMap[chain] - const { data } = await getPoolQuery(chain, id) + const { data, error } = await getPoolQuery(chain, id) - if (!data.pool) { - return Pool with id not found ({id}) + if (error) { + let alert = ( + + ) + if (error?.message === 'Pool with id does not exist') { + const error = `Pool with id not found in ${chain} (${id})` + alert = + } + return {alert} } + if (!data) return null + return ( }> diff --git a/lib/shared/components/alerts/BalAlert.tsx b/lib/shared/components/alerts/BalAlert.tsx index 407a83a36..5d9c347e3 100644 --- a/lib/shared/components/alerts/BalAlert.tsx +++ b/lib/shared/components/alerts/BalAlert.tsx @@ -10,6 +10,7 @@ export type BalAlertProps = { isSoftWarning?: boolean isNavAlert?: boolean onClose?: MouseEventHandler + ssr?: boolean } export function BalAlert({ @@ -18,13 +19,21 @@ export function BalAlert({ learnMoreLink, isSoftWarning = false, isNavAlert = false, + ssr = false, // Use true whe rendering alerts on the server side onClose, }: BalAlertProps) { return ( - + {ssr ? : } - + {content} {learnMoreLink && More} diff --git a/lib/shared/components/containers/DefaultPageContainer.tsx b/lib/shared/components/containers/DefaultPageContainer.tsx index 28ffbc917..e284fe3e8 100644 --- a/lib/shared/components/containers/DefaultPageContainer.tsx +++ b/lib/shared/components/containers/DefaultPageContainer.tsx @@ -11,7 +11,7 @@ export function DefaultPageContainer({ ...rest }: PropsWithChildren & ContainerProps & Props) { return ( - + Date: Thu, 29 Aug 2024 16:58:10 +0200 Subject: [PATCH 02/11] fix: avoid alert var --- .../pools/[chain]/[variant]/[id]/layout.tsx | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx index fe87f34a9..e16bb939e 100644 --- a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx +++ b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx @@ -63,18 +63,23 @@ export default async function PoolLayout({ params: { id, chain, variant }, child const { data, error } = await getPoolQuery(chain, id) if (error) { - let alert = ( - - ) if (error?.message === 'Pool with id does not exist') { const error = `Pool with id not found in ${chain} (${id})` - alert = + return ( + + + + ) } - return {alert} + return ( + + + + ) } if (!data) return null From 1c0d6129cde940a3a7e940d5b92a6c0f54e3073d Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Thu, 29 Aug 2024 17:04:07 +0200 Subject: [PATCH 03/11] fix: capture pool query backend errors in sentry --- app/(app)/pools/[chain]/[variant]/[id]/layout.tsx | 2 ++ sentry.server.config.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx index e16bb939e..f7663658d 100644 --- a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx +++ b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx @@ -12,6 +12,7 @@ import { arrayToSentence } from '@/lib/shared/utils/strings' import { DefaultPageContainer } from '@/lib/shared/components/containers/DefaultPageContainer' import { BalAlert } from '@/lib/shared/components/alerts/BalAlert' import { ensureError } from '@/lib/shared/utils/errors' +import { captureException } from '@sentry/nextjs' type Props = PropsWithChildren<{ params: Omit & { chain: ChainSlug } @@ -71,6 +72,7 @@ export default async function PoolLayout({ params: { id, chain, variant }, child ) } + captureException(error, { level: 'fatal' }) return ( Date: Fri, 30 Aug 2024 12:55:17 +0200 Subject: [PATCH 04/11] Update app/(app)/pools/[chain]/[variant]/[id]/layout.tsx Co-authored-by: Gareth Fuller --- app/(app)/pools/[chain]/[variant]/[id]/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx index f7663658d..74d62ecf9 100644 --- a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx +++ b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx @@ -77,7 +77,7 @@ export default async function PoolLayout({ params: { id, chain, variant }, child From 3dc07e23330114997d7c0aab290c807f5045fb72 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Fri, 30 Aug 2024 12:55:38 +0200 Subject: [PATCH 05/11] Update app/(app)/pools/[chain]/[variant]/[id]/layout.tsx Co-authored-by: Gareth Fuller --- app/(app)/pools/[chain]/[variant]/[id]/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx index 74d62ecf9..0a53fa175 100644 --- a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx +++ b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx @@ -65,7 +65,7 @@ export default async function PoolLayout({ params: { id, chain, variant }, child if (error) { if (error?.message === 'Pool with id does not exist') { - const error = `Pool with id not found in ${chain} (${id})` + const error = `Pool not found on ${chain}, ID (${id})` return ( From 8ea329be68378769c77988f0e57384a9c1d6ec57 Mon Sep 17 00:00:00 2001 From: Gareth Fuller Date: Mon, 2 Sep 2024 16:44:42 +0100 Subject: [PATCH 06/11] chore: Use error boundaries --- .../pools/[chain]/[variant]/[id]/layout.tsx | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx index 0a53fa175..0c73f4f29 100644 --- a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx +++ b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx @@ -9,8 +9,6 @@ import { Metadata } from 'next' import { PoolProvider } from '@/lib/modules/pool/PoolProvider' import { getProjectConfig } from '@/lib/config/getProjectConfig' import { arrayToSentence } from '@/lib/shared/utils/strings' -import { DefaultPageContainer } from '@/lib/shared/components/containers/DefaultPageContainer' -import { BalAlert } from '@/lib/shared/components/alerts/BalAlert' import { ensureError } from '@/lib/shared/utils/errors' import { captureException } from '@sentry/nextjs' @@ -65,23 +63,13 @@ export default async function PoolLayout({ params: { id, chain, variant }, child if (error) { if (error?.message === 'Pool with id does not exist') { - const error = `Pool not found on ${chain}, ID (${id})` - return ( - - - - ) + const error = `Pool not found on ${chain}, ID: ${id}` + throw new Error(error) } + captureException(error, { level: 'fatal' }) - return ( - - - - ) + + throw new Error('Failed to fetch pool') } if (!data) return null From b160f6ef4bc6c7b54e0dea56ecdf29b7bec73466 Mon Sep 17 00:00:00 2001 From: Gareth Fuller Date: Wed, 4 Sep 2024 14:17:25 +0100 Subject: [PATCH 07/11] chore: Minor updates --- app/(app)/pools/[chain]/[variant]/[id]/layout.tsx | 6 +----- lib/shared/components/errors/ErrorBoundary.tsx | 11 ++++------- lib/shared/utils/query-errors.ts | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx index 0c73f4f29..329aac68e 100644 --- a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx +++ b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx @@ -10,7 +10,6 @@ import { PoolProvider } from '@/lib/modules/pool/PoolProvider' import { getProjectConfig } from '@/lib/config/getProjectConfig' import { arrayToSentence } from '@/lib/shared/utils/strings' import { ensureError } from '@/lib/shared/utils/errors' -import { captureException } from '@sentry/nextjs' type Props = PropsWithChildren<{ params: Omit & { chain: ChainSlug } @@ -63,12 +62,9 @@ export default async function PoolLayout({ params: { id, chain, variant }, child if (error) { if (error?.message === 'Pool with id does not exist') { - const error = `Pool not found on ${chain}, ID: ${id}` - throw new Error(error) + throw new Error(`Pool not found on ${chain}, ID: ${id}`) } - captureException(error, { level: 'fatal' }) - throw new Error('Failed to fetch pool') } diff --git a/lib/shared/components/errors/ErrorBoundary.tsx b/lib/shared/components/errors/ErrorBoundary.tsx index 164476160..f94d64723 100644 --- a/lib/shared/components/errors/ErrorBoundary.tsx +++ b/lib/shared/components/errors/ErrorBoundary.tsx @@ -4,14 +4,11 @@ import { FallbackProps } from 'react-error-boundary' import { Button, Box, Text, Heading, VStack } from '@chakra-ui/react' import { ensureError } from '../../utils/errors' import { DefaultPageContainer } from '../containers/DefaultPageContainer' +import { captureSentryError } from '../../utils/query-errors' + +export function BoundaryError({ error, resetErrorBoundary }: FallbackProps) { + captureSentryError(error, { errorMessage: error.message }) -export function BoundaryError({ - error, - resetErrorBoundary, -}: { - error: Error & { digest?: string } - resetErrorBoundary: () => void -}) { const _error = ensureError(error) return ( diff --git a/lib/shared/utils/query-errors.ts b/lib/shared/utils/query-errors.ts index 739a3fd0c..9eb470fad 100644 --- a/lib/shared/utils/query-errors.ts +++ b/lib/shared/utils/query-errors.ts @@ -20,7 +20,7 @@ import { SwapHandler } from '@/lib/modules/swap/handlers/Swap.handler' export type SentryMetadata = { errorMessage: string errorName?: string - context: Partial + context?: Partial } export function sentryMetaForAddLiquidityHandler(errorMessage: string, params: AddLiquidityParams) { From 60de6eabd97bd0dc041f80190e23bb51d60bf168 Mon Sep 17 00:00:00 2001 From: Gareth Fuller Date: Fri, 13 Sep 2024 11:54:00 +0100 Subject: [PATCH 08/11] chore: Remove --- lib/shared/components/containers/DefaultPageContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/shared/components/containers/DefaultPageContainer.tsx b/lib/shared/components/containers/DefaultPageContainer.tsx index e284fe3e8..28ffbc917 100644 --- a/lib/shared/components/containers/DefaultPageContainer.tsx +++ b/lib/shared/components/containers/DefaultPageContainer.tsx @@ -11,7 +11,7 @@ export function DefaultPageContainer({ ...rest }: PropsWithChildren & ContainerProps & Props) { return ( - + Date: Fri, 13 Sep 2024 13:53:16 +0100 Subject: [PATCH 09/11] chore: Handle errors better --- .../pools/[chain]/[variant]/[id]/layout.tsx | 7 ++-- app/not-found.tsx | 35 +++++++++++++++++++ .../components/errors/ErrorBoundary.tsx | 17 +++++++-- lib/shared/utils/errors.ts | 2 +- 4 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 app/not-found.tsx diff --git a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx index 329aac68e..ed5cc0d20 100644 --- a/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx +++ b/app/(app)/pools/[chain]/[variant]/[id]/layout.tsx @@ -10,6 +10,7 @@ import { PoolProvider } from '@/lib/modules/pool/PoolProvider' import { getProjectConfig } from '@/lib/config/getProjectConfig' import { arrayToSentence } from '@/lib/shared/utils/strings' import { ensureError } from '@/lib/shared/utils/errors' +import { notFound } from 'next/navigation' type Props = PropsWithChildren<{ params: Omit & { chain: ChainSlug } @@ -62,14 +63,14 @@ export default async function PoolLayout({ params: { id, chain, variant }, child if (error) { if (error?.message === 'Pool with id does not exist') { - throw new Error(`Pool not found on ${chain}, ID: ${id}`) + notFound() } + throw new Error('Failed to fetch pool') + } else if (!data) { throw new Error('Failed to fetch pool') } - if (!data) return null - return ( }> diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 000000000..f612fa27f --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,35 @@ +import { DefaultPageContainer } from '@/lib/shared/components/containers/DefaultPageContainer' +import { Button, Heading, VStack, Text } from '@chakra-ui/react' +import { headers } from 'next/headers' +import Link from 'next/link' + +export default function NotFound() { + const headersList = headers() + + const poolIdSegment = 6 + const maybePoolId = headersList.get('referer')?.split('/')[poolIdSegment] + const isPoolPageNotFound = maybePoolId?.startsWith('0x') + + const title = isPoolPageNotFound ? 'Pool Not Found' : 'Page Not Found' + const description = isPoolPageNotFound + ? `The pool you are looking for does not exist: ${maybePoolId}` + : 'The page you are looking for does not exist' + + const redirectUrl = isPoolPageNotFound ? `/pools` : '/' + const redirectText = isPoolPageNotFound ? 'View All Pools' : 'Return Home' + + return ( + + + {title} + + {description} + + + + + + ) +} diff --git a/lib/shared/components/errors/ErrorBoundary.tsx b/lib/shared/components/errors/ErrorBoundary.tsx index f94d64723..db4a8d6eb 100644 --- a/lib/shared/components/errors/ErrorBoundary.tsx +++ b/lib/shared/components/errors/ErrorBoundary.tsx @@ -6,7 +6,15 @@ import { ensureError } from '../../utils/errors' import { DefaultPageContainer } from '../containers/DefaultPageContainer' import { captureSentryError } from '../../utils/query-errors' -export function BoundaryError({ error, resetErrorBoundary }: FallbackProps) { +type ErrorWithDigest = Error & { + digest?: string +} + +interface BoundaryErrorProps extends FallbackProps { + error: ErrorWithDigest +} + +export function BoundaryError({ error, resetErrorBoundary }: BoundaryErrorProps) { captureSentryError(error, { errorMessage: error.message }) const _error = ensureError(error) @@ -22,6 +30,9 @@ export function BoundaryError({ error, resetErrorBoundary }: FallbackProps) { : _error?.shortMessage || ''} {_error?.message} + + Error Digest: {_error?.digest} (this can be passed on to support to help with debugging) +