Skip to content

Commit

Permalink
update protocol validation return types for better code structure
Browse files Browse the repository at this point in the history
  • Loading branch information
jthrilly committed Nov 8, 2023
1 parent ff91a03 commit 015245e
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 82 deletions.
123 changes: 66 additions & 57 deletions app/(dashboard)/dashboard/_components/ProtocolUploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import { useRouter } from 'next/navigation';
import { DatabaseError } from '~/utils/databaseError';
import { ensureError } from '~/utils/ensureError';
import { ValidationError } from '@codaco/protocol-validation';
import Link from 'next/link';
import { ErrorDetails } from '~/components/ErrorDetails';
import { XCircle } from 'lucide-react';
import Link from '~/components/Link';
import { AlertDescription } from '~/components/ui/Alert';

type ErrorState = {
title: string;
Expand All @@ -39,6 +41,7 @@ type ProgressState = {

export default function ProtocolUploader() {
const router = useRouter();
const utils = api.useUtils();
const [error, setError] = useState<ErrorState | null>(null);
const [progress, setProgress] = useState<ProgressState | null>(null);
const { toast } = useToast();
Expand Down Expand Up @@ -77,13 +80,62 @@ export default function ProtocolUploader() {
);

// This function will throw on validation errors, with type ValidationError
await validateProtocol(protocolJson);
const validationResult = await validateProtocol(protocolJson);

if (!validationResult.isValid) {
// eslint-disable-next-line no-console
console.log('validationResult', validationResult);

setError({
title: 'The protocol is invalid!',
description: (
<>
<AlertDescription>
The protocol you uploaded is invalid. See the details below
for specific validation errors that were found.
</AlertDescription>
<AlertDescription>
If you believe that your protocol should be valid please ask
for help via our{' '}
<Link
href="https://community.networkcanvas.com"
target="_blank"
>
community forum
</Link>
.
</AlertDescription>
</>
),
additionalContent: (
<ErrorDetails>
<ul className="max-w-md list-inside space-y-2 text-white">
{[
...validationResult.schemaErrors,
...validationResult.logicErrors,
].map((validationError, i) => (
<li className="flex capitalize" key={i}>
<XCircle className="mr-2 h-4 w-4 fill-red-500 stroke-white" />
<span>
{validationError.message}{' '}
<span className="text-xs italic text-gray-500">
({validationError.path})
</span>
</span>
</li>
))}
</ul>
</ErrorDetails>
),
});

setProgress(null);
return;
}

// After this point, assume the protocol is valid.
const assets = await getProtocolAssets(protocolJson, zip);

console.log('assets', assets);

setProgress({
percent: 0,
status: 'Uploading assets...',
Expand Down Expand Up @@ -151,59 +203,18 @@ export default function ProtocolUploader() {

setProgress(null);
await clientRevalidateTag('protocol.get.all');
await utils.protocol.get.all.invalidate();
router.refresh();
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);

const error = ensureError(e);

// Validation errors come from @codaco/protocol-validation
if (error instanceof ValidationError) {
setError({
title: 'Protocol was invalid!',
description: (
<>
<p>
The protocol you uploaded was invalid. Please see the details
below for specific validation errors that were found.
</p>
<p>
If you believe that your protocol should be valid please ask
for help via our{' '}
<Link
href="https://community.networkcanvas.com"
target="_blank"
>
community forum
</Link>
.
</p>
</>
),
additionalContent: (
<ErrorDetails>
<>
<p>{error.message}</p>
<p>Errors:</p>
<ul>
{error.logicErrors.map((e, i) => (
<li key={i}>{e}</li>
))}
{error.schemaErrors.map((e, i) => (
<li key={i}>{e}</li>
))}
</ul>
</>
</ErrorDetails>
),
});
}
// Database errors are thrown inside our tRPC router
else if (error instanceof DatabaseError) {
if (error instanceof DatabaseError) {
setError({
title: 'Database error during protocol import',
description: error.message,
description: <AlertDescription>{error.message}</AlertDescription>,
additionalContent: (
<ErrorDetails>
<pre>{error.originalError.toString()}</pre>
Expand All @@ -213,17 +224,15 @@ export default function ProtocolUploader() {
} else {
setError({
title: 'Error importing protocol',
description:
'There was an unknown error while importing your protocol. The information below might help us to debug the issue.',
description: (
<AlertDescription>
There was an unknown error while importing your protocol. The
information below might help us to debug the issue.
</AlertDescription>
),
additionalContent: (
<ErrorDetails>
<pre>
<strong>Message: </strong>
{error.message}

<strong>Stack: </strong>
{error.stack}
</pre>
<pre className="whitespace-pre-wrap">{error.message}</pre>
</ErrorDetails>
),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ export const ProtocolsTable = ({
}: {
initialData: ProtocolWithInterviews[];
}) => {
const { isLoading, data: protocols } = api.protocol.get.all.useQuery(
undefined,
{
initialData,
refetchOnMount: false,
onError(error) {
throw new Error(error.message);
},
const { data: protocols } = api.protocol.get.all.useQuery(undefined, {
initialData,
refetchOnMount: false,
onError(error) {
throw new Error(error.message);
},
);
});

const [showAlertDialog, setShowAlertDialog] = useState(false);
const [protocolsToDelete, setProtocolsToDelete] =
Expand All @@ -35,7 +32,6 @@ export const ProtocolsTable = ({

return (
<>
{isLoading && <div>Loading...</div>}
<DataTable
columns={ProtocolColumns()}
data={protocols}
Expand Down
8 changes: 2 additions & 6 deletions app/(dashboard)/dashboard/_components/UserMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
'use client';

import { Loader2 } from 'lucide-react';
import { Button } from '~/components/ui/Button';
import { useSession } from '~/providers/SessionProvider';

const UserMenu = () => {
const { signOut, isLoading } = useSession();
const { signOut } = useSession();

return (
<div className="flex flex-row items-center gap-6">
<Button onClick={() => void signOut()} disabled={isLoading}>
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
Sign out
</Button>
<Button onClick={() => void signOut()}>Sign out</Button>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion components/ErrorDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { PropsWithChildren } from 'react';

export const ErrorDetails = (props: PropsWithChildren) => {
return (
<div className="max-h-52 overflow-y-auto rounded-md border bg-primary text-sm text-white [&_pre]:whitespace-pre-wrap [&_pre]:p-6 ">
<div className="max-h-52 overflow-y-auto rounded-md border bg-primary px-6 py-4 text-sm text-white">
{props.children}
</div>
);
Expand Down
4 changes: 1 addition & 3 deletions components/ui/ErrorDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ const ErrorDialog = ({
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="text-red-500">{title}</AlertDialogTitle>
{description && (
<AlertDialogDescription>{description}</AlertDialogDescription>
)}
{description}
</AlertDialogHeader>
{additionalContent}
<AlertDialogFooter>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@codaco/protocol-validation": "3.0.0-alpha.2",
"@codaco/protocol-validation": "3.0.0-alpha.4",
"@codaco/shared-consts": "^0.0.2",
"@headlessui/react": "^1.7.17",
"@hookform/resolvers": "^3.3.2",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 015245e

Please sign in to comment.