Skip to content

Commit

Permalink
Migrate remaining schemas to zod
Browse files Browse the repository at this point in the history
  • Loading branch information
dcramer committed May 20, 2023
1 parent 383f60c commit 85c0980
Show file tree
Hide file tree
Showing 63 changed files with 994 additions and 1,457 deletions.
40 changes: 0 additions & 40 deletions apps/api/src/app.ts
Expand Up @@ -9,26 +9,6 @@ import { router } from "./routes";
import { initSentry } from "./instruments";
import FastifySentry from "./sentryPlugin";

import {
bottleSchema,
newBottleSchema,
updateBottleSchema,
} from "./schemas/bottle";
import { collectionSchema } from "./schemas/collection";
import { commentSchema, newCommentSchema } from "./schemas/comment";
import { editionSchema, newEditionSchema } from "./schemas/edition";
import {
entitySchema,
newEntitySchema,
updateEntitySchema,
} from "./schemas/entity";
import { error401Schema } from "./schemas/errors";
import { followingSchema } from "./schemas/follow";
import { notificationSchema } from "./schemas/notification";
import pagingSchema from "./schemas/paging";
import { tastingSchema } from "./schemas/tasting";
import { updateUserSchema, userSchema } from "./schemas/user";

initSentry({
dsn: config.SENTRY_DSN,
release: config.VERSION,
Expand Down Expand Up @@ -83,26 +63,6 @@ export default async function buildFastify(options = {}) {
});
app.register(FastifyCors, { credentials: true, origin: config.CORS_HOST });

app.addSchema(bottleSchema);
app.addSchema(newBottleSchema);
app.addSchema(updateBottleSchema);
app.addSchema(entitySchema);
app.addSchema(newEntitySchema);
app.addSchema(updateEntitySchema);
app.addSchema(followingSchema);
app.addSchema(pagingSchema);
app.addSchema(userSchema);
app.addSchema(updateUserSchema);
app.addSchema(notificationSchema);
app.addSchema(tastingSchema);
app.addSchema(collectionSchema);
app.addSchema(commentSchema);
app.addSchema(newCommentSchema);
app.addSchema(editionSchema);
app.addSchema(newEditionSchema);

app.addSchema(error401Schema);

app.register(router);
app.register(FastifySentry);

Expand Down
9 changes: 7 additions & 2 deletions apps/api/src/lib/db.ts
Expand Up @@ -4,7 +4,12 @@ import { Entity, EntityType, NewEntity, changes, entities } from "../db/schema";

export type EntityInput =
| number
| { name: string; country: string; region?: string };
| {
name: string;
country?: string;
region?: string;
type?: ("brand" | "bottler" | "distiller")[];
};

export type UpsertOutcome<T> =
| {
Expand Down Expand Up @@ -53,7 +58,7 @@ export const upsertEntity = async ({
name: data.name,
country: data.country || null,
region: data.region || null,
type: [type],
type: Array.from(new Set([type, ...(data.type || [])])),
createdById: userId,
})
.onConflictDoNothing()
Expand Down
13 changes: 9 additions & 4 deletions apps/api/src/lib/serializers/notification.ts
Expand Up @@ -18,9 +18,13 @@ import { UserSerializer } from "./user";

export const NotificationSerializer: Serializer<Notification> = {
attrs: async (itemList: Notification[], currentUser: User) => {
const fromUserIds = itemList
.filter((i) => Boolean(i.fromUserId))
.map<number>((i) => i.fromUserId as number);
const fromUserIds = Array.from(
new Set(
itemList
.filter((i) => Boolean(i.fromUserId))
.map<number>((i) => i.fromUserId as number),
),
);

const fromUserList = fromUserIds.length
? await db.select().from(users).where(inArray(users.id, fromUserIds))
Expand Down Expand Up @@ -129,12 +133,13 @@ export const NotificationSerializer: Serializer<Notification> = {

item: (item: Notification, attrs: Record<string, any>, currentUser: User) => {
return {
id: `${item.id}`,
id: item.id,
objectType: item.objectType,
objectId: item.objectId,
createdAt: item.createdAt,
fromUser: attrs.fromUser,
ref: attrs.ref,
read: item.read,
};
},
};
5 changes: 2 additions & 3 deletions apps/api/src/lib/serializers/tasting.ts
Expand Up @@ -67,8 +67,7 @@ export const TastingSerializer: Serializer<Tasting> = {
);

const editionList = results.map((r) => r.edition).filter(notEmpty);

const editionsByRef = Object.fromEntries(
const editionsById = Object.fromEntries(
(await serialize(EditionSerializer, editionList, currentUser)).map(
(data, index) => [editionList[index].id, data],
),
Expand All @@ -80,7 +79,7 @@ export const TastingSerializer: Serializer<Tasting> = {
item.id,
{
hasToasted: userToastsList.indexOf(item.id) !== -1,
edition: item.editionId ? editionsByRef[item.id] : null,
edition: item.editionId ? editionsById[item.editionId] : null,
createdBy: usersByRef[item.id] || null,
bottle: bottlesByRef[item.id] || null,
},
Expand Down
59 changes: 26 additions & 33 deletions apps/api/src/routes/addBottle.ts
@@ -1,38 +1,23 @@
import { BottleInputSchema, BottleSchema } from "@peated/shared/schemas";
import { eq, inArray, sql } from "drizzle-orm";
import type { RouteOptions } from "fastify";
import { IncomingMessage, Server, ServerResponse } from "http";
import { z } from "zod";
import zodToJsonSchema from "zod-to-json-schema";
import { db } from "../db";
import {
Category,
bottles,
bottlesToDistillers,
changes,
entities,
} from "../db/schema";
import { EntityInput, upsertEntity } from "../lib/db";
import { bottles, bottlesToDistillers, changes, entities } from "../db/schema";
import { upsertEntity } from "../lib/db";
import { serialize } from "../lib/serializers";
import { BottleSerializer } from "../lib/serializers/bottle";
import { requireAuth } from "../middleware/auth";

type BottleInput = {
name: string;
category: Category;
brand: EntityInput;
distillers: EntityInput[];
statedAge?: number;
};

export default {
method: "POST",
url: "/bottles",
schema: {
body: {
$ref: "/schemas/newBottle",
},
body: zodToJsonSchema(BottleInputSchema),
response: {
201: {
$ref: "/schemas/bottle",
},
201: zodToJsonSchema(BottleSchema),
},
},
preHandler: [requireAuth],
Expand Down Expand Up @@ -68,16 +53,24 @@ export default {
});
}

const [bottle] = await tx
.insert(bottles)
.values({
name: body.name,
statedAge: body.statedAge || null,
category: body.category || null,
brandId: brand.id,
createdById: req.user.id,
})
.returning();
try {
const [bottle] = await tx
.insert(bottles)
.values({
name: body.name,
statedAge: body.statedAge || null,
category: body.category || null,
brandId: brand.id,
createdById: req.user.id,
})
.returning();
} catch (err: any) {
if (err?.code === "23505" && err?.constraint === "bottle_brand_unq") {
return res
.status(409)
.send({ error: "Bottle with name already exists under brand." });
}
}

const distillerIds: number[] = [];
if (body.distillers)
Expand Down Expand Up @@ -131,6 +124,6 @@ export default {
IncomingMessage,
ServerResponse,
{
Body: BottleInput;
Body: z.infer<typeof BottleInputSchema>;
}
>;
15 changes: 7 additions & 8 deletions apps/api/src/routes/addEntity.ts
@@ -1,6 +1,9 @@
import { EntityInputSchema, EntitySchema } from "@peated/shared/schemas";
import { eq } from "drizzle-orm";
import type { RouteOptions } from "fastify";
import { IncomingMessage, Server, ServerResponse } from "http";
import { z } from "zod";
import zodToJsonSchema from "zod-to-json-schema";
import { db } from "../db";
import { NewEntity, changes, entities } from "../db/schema";
import { serialize } from "../lib/serializers";
Expand All @@ -11,13 +14,9 @@ export default {
method: "POST",
url: "/entities",
schema: {
body: {
$ref: "/schemas/newEntity",
},
body: zodToJsonSchema(EntityInputSchema),
response: {
201: {
$ref: "/schemas/entity",
},
201: zodToJsonSchema(EntitySchema),
},
},
preHandler: [requireMod],
Expand Down Expand Up @@ -69,7 +68,7 @@ export default {
});

if (!entity) {
return res.status(409).send("Unable to create entity");
return res.status(409).send({ error: "Unable to create entity" });
}

res.status(201).send(await serialize(EntitySerializer, entity, req.user));
Expand All @@ -79,6 +78,6 @@ export default {
IncomingMessage,
ServerResponse,
{
Body: NewEntity;
Body: z.infer<typeof EntityInputSchema>;
}
>;
10 changes: 4 additions & 6 deletions apps/api/src/routes/addTasting.ts
Expand Up @@ -4,7 +4,7 @@ import { IncomingMessage, Server, ServerResponse } from "http";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";

import { NewTasting } from "@peated/shared/schemas";
import { TastingInputSchema, TastingSchema } from "@peated/shared/schemas";

import { db } from "../db";
import {
Expand All @@ -23,11 +23,9 @@ export default {
method: "POST",
url: "/tastings",
schema: {
body: zodToJsonSchema(NewTasting),
body: zodToJsonSchema(TastingInputSchema),
response: {
201: {
$ref: "/schemas/tasting",
},
201: zodToJsonSchema(TastingSchema),
},
},
preHandler: [requireAuth],
Expand Down Expand Up @@ -160,6 +158,6 @@ export default {
IncomingMessage,
ServerResponse,
{
Body: z.infer<typeof NewTasting>;
Body: z.infer<typeof TastingInputSchema>;
}
>;
10 changes: 4 additions & 6 deletions apps/api/src/routes/addTastingComment.ts
@@ -1,6 +1,8 @@
import { CommentInputSchema, CommentSchema } from "@peated/shared/schemas";
import { eq, sql } from "drizzle-orm";
import type { RouteOptions } from "fastify";
import { IncomingMessage, Server, ServerResponse } from "http";
import zodToJsonSchema from "zod-to-json-schema";
import { db, first } from "../db";
import { Comment, NewComment, comments, tastings } from "../db/schema";
import { isDistantFuture, isDistantPast } from "../lib/dates";
Expand All @@ -20,13 +22,9 @@ export default {
tastingId: { type: "number" },
},
},
body: {
$ref: "/schemas/newComment",
},
body: zodToJsonSchema(CommentInputSchema),
response: {
201: {
$ref: "/schemas/comment",
},
201: zodToJsonSchema(CommentSchema),
},
},
preHandler: [requireAuth],
Expand Down
14 changes: 3 additions & 11 deletions apps/api/src/routes/authBasic.ts
@@ -1,8 +1,10 @@
import type { RouteOptions } from "fastify";
import { IncomingMessage, Server, ServerResponse } from "http";

import { AuthSchema } from "@peated/shared/schemas";
import { compareSync } from "bcrypt";
import { eq } from "drizzle-orm";
import zodToJsonSchema from "zod-to-json-schema";
import { db } from "../db";
import { users } from "../db/schema";
import { createAccessToken } from "../lib/auth";
Expand All @@ -22,17 +24,7 @@ export default {
},
},
response: {
200: {
type: "object",
required: ["user", "accessToken"],
properties: {
user: { $ref: "/schemas/user" },
accessToken: { type: "string" },
},
},
401: {
$ref: "/errors/401",
},
200: zodToJsonSchema(AuthSchema),
},
},
handler: async function (req, res) {
Expand Down
13 changes: 3 additions & 10 deletions apps/api/src/routes/authDetails.ts
@@ -1,7 +1,9 @@
import type { RouteOptions } from "fastify";
import { IncomingMessage, Server, ServerResponse } from "http";

import { AuthSchema } from "@peated/shared/schemas";
import { eq } from "drizzle-orm";
import zodToJsonSchema from "zod-to-json-schema";
import { db } from "../db";
import { users } from "../db/schema";
import { serialize } from "../lib/serializers";
Expand All @@ -14,16 +16,7 @@ export default {
preHandler: [requireAuth],
schema: {
response: {
200: {
type: "object",
required: ["user"],
properties: {
user: { $ref: "/schemas/user" },
},
},
401: {
$ref: "/errors/401",
},
200: zodToJsonSchema(AuthSchema),
},
},
handler: async function (req, res) {
Expand Down

0 comments on commit 85c0980

Please sign in to comment.