Skip to content

Commit

Permalink
fix: handle backend errors in pool page (#1031)
Browse files Browse the repository at this point in the history
* fix: pool page error when pool id is not found

* fix: avoid alert var

* fix: capture pool query backend errors in sentry

* Update app/(app)/pools/[chain]/[variant]/[id]/layout.tsx

Co-authored-by: Gareth Fuller <[email protected]>

* Update app/(app)/pools/[chain]/[variant]/[id]/layout.tsx

Co-authored-by: Gareth Fuller <[email protected]>

* chore: Use error boundaries

* chore: Minor updates

* chore: Remove

* chore: Handle errors better

* chore: Update container height

* fix: Await header

---------

Co-authored-by: Gareth Fuller <[email protected]>
  • Loading branch information
agualis and garethfuller committed Sep 16, 2024
1 parent 128056b commit deede8c
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 28 deletions.
41 changes: 26 additions & 15 deletions app/(app)/pools/[chain]/[variant]/[id]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ 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 { ensureError } from '@/lib/shared/utils/errors'
import { notFound } from 'next/navigation'

type Props = PropsWithChildren<{
params: Omit<FetchPoolProps, 'chain'> & { chain: ChainSlug }
Expand All @@ -21,24 +22,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<Metadata> {
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))
Expand All @@ -54,10 +59,16 @@ 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 (error) {
if (error?.message === 'Pool with id does not exist') {
notFound()
}

if (!data.pool) {
return <Box>Pool with id not found ({id})</Box>
throw new Error('Failed to fetch pool')
} else if (!data) {
throw new Error('Failed to fetch pool')
}

return (
Expand Down
37 changes: 37 additions & 0 deletions app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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 async function NotFound() {
const headersList = headers()

const referer = await headersList.get('referer')

const poolIdSegment = 6
const maybePoolId = 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 (
<DefaultPageContainer minH="80vh">
<VStack align="start" spacing="md">
<Heading size="md">{title}</Heading>
<VStack align="start" spacing="xs">
<Text>{description}</Text>
</VStack>

<Button as={Link} size="sm" href={redirectUrl}>
{redirectText}
</Button>
</VStack>
</DefaultPageContainer>
)
}
13 changes: 11 additions & 2 deletions lib/shared/components/alerts/BalAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type BalAlertProps = {
isSoftWarning?: boolean
isNavAlert?: boolean
onClose?: MouseEventHandler
ssr?: boolean
}

export function BalAlert({
Expand All @@ -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 (
<Alert status={status} rounded={isNavAlert ? 'none' : 'default'}>
<AlertIcon as={getAlertIcon(status)} />
{ssr ? <AlertIcon /> : <AlertIcon as={getAlertIcon(status)} />}

<AlertTitle gap={1} display="flex" w="full" color="black">
<AlertTitle
gap={1}
display="flex"
w="full"
color="black"
wordBreak="break-word"
flexDirection="column"
>
{content}
</AlertTitle>
{learnMoreLink && <BalAlertButtonLink href={learnMoreLink}>More</BalAlertButtonLink>}
Expand Down
26 changes: 17 additions & 9 deletions lib/shared/components/errors/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ 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'

type ErrorWithDigest = Error & {
digest?: string
}

interface BoundaryErrorProps extends FallbackProps {
error: ErrorWithDigest
}

export function BoundaryError({ error, resetErrorBoundary }: BoundaryErrorProps) {
captureSentryError(error, { errorMessage: error.message })

export function BoundaryError({
error,
resetErrorBoundary,
}: {
error: Error & { digest?: string }
resetErrorBoundary: () => void
}) {
const _error = ensureError(error)

return (
Expand All @@ -25,6 +30,9 @@ export function BoundaryError({
: _error?.shortMessage || ''}
</Text>
<Text>{_error?.message}</Text>
<Text>
Error Digest: {_error?.digest} (this can be passed on to support to help with debugging)
</Text>
</VStack>

<Button size="sm" onClick={resetErrorBoundary}>
Expand All @@ -35,9 +43,9 @@ export function BoundaryError({
)
}

export function PageErrorBoundary(props: FallbackProps) {
export function PageErrorBoundary(props: BoundaryErrorProps) {
return (
<DefaultPageContainer>
<DefaultPageContainer minH="80vh">
<BoundaryError {...props} />
</DefaultPageContainer>
)
Expand Down
2 changes: 1 addition & 1 deletion lib/shared/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class SentryError extends Error {
}

// Ensures returned value is an Error type.
export function ensureError(value: unknown): Error & { shortMessage?: string } {
export function ensureError(value: unknown): Error & { shortMessage?: string; digest?: string } {
if (value instanceof Error) return value

let stringified = '[Unable to stringify thrown value]'
Expand Down
2 changes: 1 addition & 1 deletion lib/shared/utils/query-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { SwapHandler } from '@/lib/modules/swap/handlers/Swap.handler'
export type SentryMetadata = {
errorMessage: string
errorName?: string
context: Partial<ScopeContext>
context?: Partial<ScopeContext>
}

export function sentryMetaForAddLiquidityHandler(errorMessage: string, params: AddLiquidityParams) {
Expand Down
2 changes: 2 additions & 0 deletions sentry.server.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

import * as Sentry from '@sentry/nextjs'
import { sentryDSN } from './sentry.config'
import { isProd } from './lib/config/app.config'

Sentry.init({
enabled: isProd,
dsn: sentryDSN,
// Adjust this value in production, or use tracesSampler for greater control
tracesSampleRate: 0,
Expand Down

0 comments on commit deede8c

Please sign in to comment.