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

🚸 Enhanced error rendering in the ErrorDisplay component, adding detailed error summaries #446

Merged
merged 3 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all 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: 6 additions & 0 deletions .changeset/wild-crews-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@liam-hq/erd-core": patch
"@liam-hq/cli": patch
---

🚸 Enhanced error rendering in the `ErrorDisplay` component, adding detailed error summaries
14 changes: 11 additions & 3 deletions frontend/apps/erd-web/app/erd/p/[...slug]/erdViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@ import {
import { useEffect } from 'react'
import * as v from 'valibot'

type ErrorObject = {
name: string
message: string
}

type ERDViewerProps = {
dbStructure: DBStructure
errors: ProcessError[]
errorObjects: ErrorObject[]
defaultSidebarOpen: boolean
}

export default function ERDViewer({
dbStructure,
errors,
errorObjects,
defaultSidebarOpen,
}: ERDViewerProps) {
useEffect(() => {
Expand All @@ -35,7 +40,10 @@ export default function ERDViewer({
return (
<div style={{ height: '100vh' }}>
<VersionProvider version={version}>
<ERDRenderer defaultSidebarOpen={defaultSidebarOpen} errors={errors} />
<ERDRenderer
defaultSidebarOpen={defaultSidebarOpen}
errorObjects={errorObjects}
/>
</VersionProvider>
</div>
)
Expand Down
13 changes: 7 additions & 6 deletions frontend/apps/erd-web/app/erd/p/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,21 @@ export default async function Page({
}

const { value: dbStructure, errors } = await parse(input, format)
if (errors.length > 0) {
for (const error of errors) {
Sentry.captureException(error)
}
for (const error of errors) {
Sentry.captureException(error)
}

const errorObjects = errors.map((error) => ({
name: error.name,
message: error.message,
}))
const cookieStore = await cookies()
const defaultSidebarOpen = cookieStore.get('sidebar:state')?.value === 'true'

return (
<ERDViewer
dbStructure={dbStructure}
defaultSidebarOpen={defaultSidebarOpen}
errors={errors}
errorObjects={errorObjects}
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('runPreprocess', () => {
])
})

it('should return an error if failed parcing schema file', async () => {
it('should return an error if failed parsing schema file', async () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'test-distDir-'))
const inputPath = path.join(tmpDir, 'input.sql')
fs.writeFileSync(inputPath, 'invalid;', 'utf8')
Expand All @@ -84,7 +84,7 @@ describe('runPreprocess', () => {
expect(outputFilePath).toBeNull()
expect(errors).toEqual([
new WarningProcessingError(
'Error during parcing schema file: syntax error at or near "invalid"',
'Error during parsing schema file: syntax error at or near "invalid"',
),
])
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export async function runPreprocess(
errors: errors.map(
(err) =>
new WarningProcessingError(
`Error during parcing schema file: ${err.message}`,
`Error during parsing schema file: ${err.message}`,
),
),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
Relationship,
Table,
} from '../../../schema/index.js'
import { UnexpectedTokenWarningError } from '../../errors.js'
import { type ProcessError, UnexpectedTokenWarningError } from '../../errors.js'
import type { ProcessResult } from '../../types.js'
import {
defaultRelationshipName,
Expand Down Expand Up @@ -101,7 +101,7 @@ const constraintToRelationship = (
export const convertToDBStructure = (stmts: RawStmt[]): ProcessResult => {
const tables: Record<string, Table> = {}
const relationships: Record<string, Relationship> = {}
const errors: Error[] = []
const errors: ProcessError[] = []

function isConstraintNode(node: Node): node is { Constraint: Constraint } {
return (node as { Constraint: Constraint }).Constraint !== undefined
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DBStructure } from '../../../schema/index.js'
import { UnexpectedTokenWarningError } from '../../errors.js'
import { type ProcessError, UnexpectedTokenWarningError } from '../../errors.js'
import type { Processor } from '../../types.js'
import { convertToDBStructure } from './converter.js'
import { mergeDBStructures } from './mergeDBStructures.js'
Expand All @@ -9,7 +9,7 @@ import { processSQLInChunks } from './processSQLInChunks.js'
export const processor: Processor = async (str: string) => {
const dbStructure: DBStructure = { tables: {}, relationships: {} }
const CHUNK_SIZE = 1000
const errors: Error[] = []
const errors: ProcessError[] = []

await processSQLInChunks(str, CHUNK_SIZE, async (chunk) => {
const { parse_tree, error: parseError } = await parse(chunk)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import '@xyflow/react/dist/style.css'
import type { ProcessError } from '@liam-hq/db-structure'
import { SidebarProvider, SidebarTrigger, ToastProvider } from '@liam-hq/ui'
import { ReactFlowProvider } from '@xyflow/react'
import { type FC, useCallback, useState } from 'react'
Expand All @@ -19,14 +18,19 @@ import { RelationshipEdgeParticleMarker } from './RelationshipEdgeParticleMarker
import { TableDetailDrawer, TableDetailDrawerRoot } from './TableDetailDrawer'
import { convertDBStructureToNodes } from './convertDBStructureToNodes'

type ErrorObject = {
name: string
message: string
}

type Props = {
defaultSidebarOpen?: boolean | undefined
errors?: ProcessError[] | undefined
errorObjects?: ErrorObject[] | undefined
}

export const ERDRenderer: FC<Props> = ({
defaultSidebarOpen = false,
errors = [],
errorObjects = [],
}) => {
const [open, setOpen] = useState(defaultSidebarOpen)

Expand Down Expand Up @@ -67,8 +71,10 @@ export const ERDRenderer: FC<Props> = ({
<SidebarTrigger />
</div>
<TableDetailDrawerRoot>
{errors.length > 0 && <ErrorDisplay errors={errors} />}
{errors.length > 0 || (
{errorObjects.length > 0 && (
<ErrorDisplay errors={errorObjects} />
)}
{errorObjects.length > 0 || (
<>
<ERDContent
key={`${nodes.length}-${showMode}`}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import type { ProcessError } from '@liam-hq/db-structure'
import { InfoIcon } from '@liam-hq/ui'
import type { FC } from 'react'
import styles from './ErrorDisplay.module.css'

type ErrorObject = {
name: string
message: string
}

type Props = {
errors: ProcessError[]
errors: ErrorObject[]
}

export const ErrorDisplay: FC<Props> = ({ errors }) => {
Expand All @@ -17,12 +21,27 @@ export const ErrorDisplay: FC<Props> = ({ errors }) => {
<div className={styles.inner}>
<div className={styles.message1}>
<div className={styles.message1Title}>
Oh no! We’ve encountered {errors.length} errors 🛸💫
Oh no! We’ve encountered some errors 🛸💫
</div>

{errors[0] && (
<div className={styles.message1Sentence}>
<details>
<summary>View errors</summary>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose not to use <details open> to keep the display compact.

<ul>
<li key={errors[0].name}>
<code>
{errors[0].name}: {errors[0].message}
</code>
</li>
</ul>
</details>
</div>
)}
<div className={styles.message1Sentence}>
<p>
It seems {errors.length} SQL statements couldn’t make it through
the parser’s orbit.
It seems some SQL statements couldn’t make it through the
parser’s orbit.
</p>
<p>
Parsing every SQL dialect is like navigating an asteroid
Expand Down
Loading