Skip to content

Commit

Permalink
Add max length to table title and slug, handle validation errors in a…
Browse files Browse the repository at this point in the history
…uto-generated table slugs
  • Loading branch information
solomonhawk committed Nov 9, 2024
1 parent cb27b0f commit 4cfa5f2
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 13 deletions.
15 changes: 13 additions & 2 deletions packages/db/src/service/table.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { slugify } from "@manifold/lib";
import {
invalidTableSlugMessage,
slug,
type TableCreateInput,
tableCreateInput,
type TableDeleteInput,
type TableGetInput,
type TableListInput,
Expand Down Expand Up @@ -47,12 +50,20 @@ export async function listTables(userId: string, input: TableListInput) {
}

export async function createTable(userId: string, input: TableCreateInput) {
const inputWithDefaultSlug = tableCreateInput
.extend({
slug: slug({ message: invalidTableSlugMessage }),
})
.parse({
...input,
slug: input.slug ?? slugify(input.title),
});

const [table] = await db
.insert(schema.tables)
.values({
...input,
...inputWithDefaultSlug,
ownerId: userId,
slug: input.slug ?? slugify(input.title),
})
.returning()
.execute();
Expand Down
34 changes: 26 additions & 8 deletions packages/router/src/error.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
import { TRPCError } from "@trpc/server";
import { ZodError } from "zod";

export function validationError({
path,
message,
}: {
type ErrorPathAndMessage = {
path: string[];
message: string;
}) {
};

type ErrorCauseAndMessage = {
cause: ZodError;
message: string;
};

export function validationError(
causeAndMessage: ErrorCauseAndMessage,
): TRPCError;
export function validationError(pathAndMessage: ErrorPathAndMessage): TRPCError;
export function validationError(
pathOrCauseAndMessage: ErrorPathAndMessage | ErrorCauseAndMessage,
): TRPCError {
if ("cause" in pathOrCauseAndMessage) {
return new TRPCError({
code: "BAD_REQUEST",
message: pathOrCauseAndMessage.message,
cause: pathOrCauseAndMessage.cause,
});
}

return new TRPCError({
code: "BAD_REQUEST",
message,
message: pathOrCauseAndMessage.message,
cause: new ZodError([
{
message,
path,
message: pathOrCauseAndMessage.message,
path: pathOrCauseAndMessage.path,
code: "custom",
},
]),
Expand Down
9 changes: 9 additions & 0 deletions packages/router/src/routers/table.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isUniqueConstraintViolation, tableService } from "@manifold/db";
import {
isValidationError,
tableCreateInput,
tableDeleteInput,
tableGetInput,
Expand Down Expand Up @@ -31,6 +32,14 @@ export const tableRouter = t.router({
});
}

if (isValidationError(e)) {
throw validationError({
cause: e,
message:
"We couldn’t generate a valid identifier for this table, please specify one explicitly",
});
}

throw e;
}
}),
Expand Down
5 changes: 5 additions & 0 deletions packages/validators/src/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ZodError } from "zod";

export function isValidationError(e: unknown): e is ZodError {
return e instanceof ZodError;
}
2 changes: 2 additions & 0 deletions packages/validators/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./error";
export * from "./schemas";
export * from "./table";
export * from "./user";
export * from "zod";
2 changes: 2 additions & 0 deletions packages/validators/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export function slug({ message }: { message: string }) {
return z
.string()
.min(1, { message: "Can’t be blank" })
.max(64, { message: "Must be 64 characters or less" })
.refine((slug) => slug === slugify(slug), {
message,
});
Expand All @@ -13,6 +14,7 @@ export function slug({ message }: { message: string }) {
export function optionalSlug({ message }: { message: string }) {
return z
.string()
.max(64, { message: "Must be 64 characters or less" })
.optional()
.transform((x) => (x === "" ? undefined : x))
.refine(
Expand Down
10 changes: 7 additions & 3 deletions packages/validators/src/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ export const tableListInput = z.object({

export type TableListInput = z.infer<typeof tableListInput>;

export const invalidTableSlugMessage =
"Identifier is invalid. Can only contain lowercase letters, numbers, and hyphens";

export const tableCreateInput = z.object({
title: z.string().min(1, { message: "Title can’t be blank" }),
title: z.string().min(1, { message: "Title can’t be blank" }).max(64, {
message: "Title must be 64 characters or less",
}),
slug: optionalSlug({
message:
"Identifier is invalid. Can only contain lowercase letters, numbers, and hyphens",
message: invalidTableSlugMessage,
}),
description: z.string().optional(),
definition: z.string(),
Expand Down

0 comments on commit 4cfa5f2

Please sign in to comment.