Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions frontend/src/utils/helpers/apolloClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
import { AppError, handleAppError } from 'app/global-error'

import { GRAPHQL_URL } from 'utils/env.client'
import { getCsrfToken } from 'utils/utility'

const createApolloClient = () => {
if (!GRAPHQL_URL) {
const error = new AppError(500, 'Missing GraphQL URL')
handleAppError(error)
// Only create the error for logging or throwing, do not emit a toast here
return null
}

Expand Down
103 changes: 92 additions & 11 deletions frontend/src/wrappers/provider.tsx
Comment thread
Saurabh-2607 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
'use client'
import { ApolloProvider } from '@apollo/client/react'
import { HeroUIProvider, ToastProvider } from '@heroui/react'
import { addToast } from '@heroui/toast'
import { useDjangoSession } from 'hooks/useDjangoSession'
import { SessionProvider } from 'next-auth/react'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import React, { Suspense } from 'react'
import { ENVIRONMENT, GRAPHQL_URL } from 'utils/env.client'
import apolloClient from 'utils/helpers/apolloClient'
import { getCsrfToken } from 'utils/utility'

// <AppInitializer> is a component that initializes the Django session.
// It ensures the session is synced with Django when the app starts.
Expand All @@ -22,24 +25,102 @@
}: Readonly<{
children: React.ReactNode
}>) {
if (!apolloClient) {
return (
<div className="flex min-h-screen items-center justify-center text-red-500">
Configuration Error: GraphQL Client failed to initialize
</div>
)
}
const isProduction = ENVIRONMENT === 'production'
const [graphQLReachability, setGraphQLReachability] = React.useState<
'pending' | 'reachable' | 'unreachable'
>('pending')
const lastToastMessageRef = React.useRef<string | null>(null)

React.useEffect(() => {
if (!apolloClient || !GRAPHQL_URL) {
setGraphQLReachability('unreachable')
return
}
const graphqlUrl = GRAPHQL_URL
const abortController = new AbortController()

const verifyGraphQLEndpoint = async () => {
try {
const csrfToken = await getCsrfToken()
const response = await fetch(graphqlUrl, {
body: JSON.stringify({ query: 'query { __typename }' }),
credentials: 'include',
headers: {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken,
},
method: 'POST',
signal: abortController.signal,
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if (!response.ok) {
setGraphQLReachability('unreachable')
return
}

setGraphQLReachability('reachable')
} catch {
if (!abortController.signal.aborted) {
setGraphQLReachability('unreachable')
}
}
}

setGraphQLReachability('pending')
void verifyGraphQLEndpoint()

return () => {
abortController.abort()
}
}, [])

const graphQLErrorMessage = !apolloClient

Check warning on line 77 in frontend/src/wrappers/provider.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unexpected negated condition.

See more on https://sonarcloud.io/project/issues?id=OWASP_Nest&issues=AZ0h87yIGkojc2Dthl4n&open=AZ0h87yIGkojc2Dthl4n&pullRequest=4361
? isProduction
? 'Something went wrong'
: 'GraphQL client setup required. Ensure backend is running and GraphQL environment variables are configured.'

Check warning on line 80 in frontend/src/wrappers/provider.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=OWASP_Nest&issues=AZ0h87yIGkojc2Dthl4q&open=AZ0h87yIGkojc2Dthl4q&pullRequest=4361
: graphQLReachability === 'unreachable'
? isProduction
? 'Something went wrong'
: 'GraphQL endpoint is unreachable. Ensure the backend service is running and NEXT_PUBLIC_GRAPHQL_URL is correct.'

Check warning on line 84 in frontend/src/wrappers/provider.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=OWASP_Nest&issues=AZ0h87yIGkojc2Dthl4o&open=AZ0h87yIGkojc2Dthl4o&pullRequest=4361
: null

Check warning on line 85 in frontend/src/wrappers/provider.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=OWASP_Nest&issues=AZ0iKPO0M7VHBcxZhIMK&open=AZ0iKPO0M7VHBcxZhIMK&pullRequest=4361

React.useEffect(() => {
if (!graphQLErrorMessage || lastToastMessageRef.current === graphQLErrorMessage) {
return
}

addToast({
color: 'danger',
description: graphQLErrorMessage,
shouldShowTimeoutProgress: true,
timeout: 5000,
title: 'Configuration Error',
variant: 'solid',
})

lastToastMessageRef.current = graphQLErrorMessage
}, [graphQLErrorMessage])

return (
<Suspense>
<SessionProvider>
<HeroUIProvider>
<NextThemesProvider attribute="class" defaultTheme="dark">
<ToastProvider />
<ApolloProvider client={apolloClient}>
<AppInitializer />
{children}
</ApolloProvider>
{graphQLReachability === 'pending' && (
<div style={{ padding: 32, textAlign: 'center' }}>Checking GraphQL endpoint…</div>
)}
{graphQLReachability === 'unreachable' && (
<div style={{ padding: 32, textAlign: 'center', color: 'red' }}>
GraphQL endpoint is unreachable.
</div>
)}
Comment thread
Saurabh-2607 marked this conversation as resolved.
Comment thread
Saurabh-2607 marked this conversation as resolved.
{apolloClient && graphQLReachability === 'reachable' ? (
<ApolloProvider client={apolloClient}>
<AppInitializer />
{children}
</ApolloProvider>
) : null}
</NextThemesProvider>
</HeroUIProvider>
</SessionProvider>
Expand Down