Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle backend errors in pool page #1031

Merged
merged 13 commits into from
Sep 13, 2024
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') {
agualis marked this conversation as resolved.
Show resolved Hide resolved
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
agualis marked this conversation as resolved.
Show resolved Hide resolved
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
Loading