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

Zod threads #149

Closed
wants to merge 3 commits into from
Closed
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
10 changes: 10 additions & 0 deletions handlers/errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NextFunction, Request, Response } from "express";

import { ApiError } from "types/errors/api";
import { ZodError } from "zod";
import debug from "debug";

const log = debug("bobaserver:handlers:errors");
Expand All @@ -20,5 +21,14 @@ export const handleApiErrors = (
});
return;
}
if (err instanceof ZodError) {
const message = `Invalid schema: [${err.issues
.map((e) => `"${e.code} (${e.path.join("/")}): ${e.message}"`)
.join(", ")}]`;
log("Sending back ZodError (500):", message);
res.status(500).json({
message,
});
}
next(err);
};
5 changes: 3 additions & 2 deletions server/threads/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import debug from "debug";
import { ensureLoggedIn } from "handlers/auth";
import express from "express";
import { moveThread } from "./queries";
import { getThreadByExternalIdResponse } from "types/open-api/generated/types";

const info = debug("bobaserver:threads:routes-info");
const log = debug("bobaserver:threads:routes-log");
Expand All @@ -44,7 +45,6 @@ const router = express.Router();
* description: Fetches data for the specified thread.
* tags:
* - /threads/
* - unzodded
* security:
* - firebase: []
* - {}
Expand Down Expand Up @@ -86,7 +86,8 @@ router.get("/:thread_id", ensureThreadAccess, async (req, res) => {
ensureNoIdentityLeakage(serverThread);

info(`sending back data for thread ${serverThread.id}.`);
res.status(200).json(serverThread);

res.status(200).json(serverThread satisfies getThreadByExternalIdResponse);
});

/**
Expand Down
16 changes: 11 additions & 5 deletions test/data/posts.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Comment, Post } from "types/rest/threads";
import { Comment, Contribution } from "types/open-api/generated/types";

export const CHARACTER_TO_MAIM_POST_ID = "11b85dac-e122-40e0-b09a-8829c5e0250e";
export const KERMIT_FRIEND_COMMENT_ID = "89fc3682-cb74-43f9-9a63-bd97d0f59bb9";

export const CHARACTER_TO_MAIM_POST: Post = {
export const CHARACTER_TO_MAIM_POST: Contribution = {
id: CHARACTER_TO_MAIM_POST_ID,
content: '[{"insert":"Favorite character to maim?"}]',
created_at: "2020-04-30T03:23:00.00Z",
Expand All @@ -21,6 +21,7 @@ export const CHARACTER_TO_MAIM_POST: Post = {
"https://firebasestorage.googleapis.com/v0/b/bobaboard-fb.appspot.com/o/images%2Fbobaland%2Fundefined%2F989f4b40-c1b8-4793-93dd-57e93df3e7ec.png?alt=media&token=cabdd8d5-b6a9-4914-bb59-eda4629f151b",
color: null,
},
user_identity: null,
total_comments_amount: 0,
tags: {
index_tags: ["evil", "bobapost"],
Expand All @@ -30,7 +31,7 @@ export const CHARACTER_TO_MAIM_POST: Post = {
},
};

export const REVOLVER_OCELOT_POST: Post = {
export const REVOLVER_OCELOT_POST: Contribution = {
id: "619adf62-833f-4bea-b591-03e807338a8e",
content: '[{"insert":"Revolver Ocelot"}]',
created_at: "2020-05-01T05:42:00.00Z",
Expand All @@ -48,6 +49,7 @@ export const REVOLVER_OCELOT_POST: Post = {
"https://firebasestorage.googleapis.com/v0/b/bobaboard-fb.appspot.com/o/images%2Fbobaland%2Fundefined%2F9b7a5d90-4885-43bf-a5f5-e861b7b87505.png?alt=media&token=83ae88ca-5c81-4d1b-9208-0a936017c485",
color: null,
},
user_identity: null,
total_comments_amount: 0,
tags: {
index_tags: ["evil", "oddly specific", "metal gear", "bobapost"],
Expand All @@ -57,7 +59,7 @@ export const REVOLVER_OCELOT_POST: Post = {
},
};

export const KERMIT_POST: Post = {
export const KERMIT_POST: Contribution = {
content: '[{"insert":"Kermit the Frog"}]',
created_at: "2020-05-02T06:04:00.00Z",
id: "b95bb260-eae0-456c-a5d0-8ae9e52608d8",
Expand All @@ -75,6 +77,7 @@ export const KERMIT_POST: Post = {
"https://firebasestorage.googleapis.com/v0/b/bobaboard-fb.appspot.com/o/images%2Fbobaland%2Fundefined%2F989f4b40-c1b8-4793-93dd-57e93df3e7ec.png?alt=media&token=cabdd8d5-b6a9-4914-bb59-eda4629f151b",
color: null,
},
user_identity: null,
total_comments_amount: 2,
tags: {
index_tags: ["good", "oddly specific", "bobapost"],
Expand Down Expand Up @@ -107,6 +110,7 @@ export const KERMIT_COMMENTS: Comment[] = [
"https://firebasestorage.googleapis.com/v0/b/bobaboard-fb.appspot.com/o/images%2Fbobaland%2Fundefined%2F9b7a5d90-4885-43bf-a5f5-e861b7b87505.png?alt=media&token=83ae88ca-5c81-4d1b-9208-0a936017c485",
color: null,
},
user_identity: null,
},
{
content: '[{"insert":"friends!!!!!"}]',
Expand All @@ -126,10 +130,11 @@ export const KERMIT_COMMENTS: Comment[] = [
"https://firebasestorage.googleapis.com/v0/b/bobaboard-fb.appspot.com/o/images%2Fbobaland%2Fundefined%2F9b7a5d90-4885-43bf-a5f5-e861b7b87505.png?alt=media&token=83ae88ca-5c81-4d1b-9208-0a936017c485",
color: null,
},
user_identity: null,
},
];

export const CANT_SEE_ME_POST: Post = {
export const CANT_SEE_ME_POST: Contribution = {
content: '[{"insert":"You can\'t see me!"}]',
created_at: "2020-04-24T05:42:00.00Z",
friend: false,
Expand All @@ -146,6 +151,7 @@ export const CANT_SEE_ME_POST: Post = {
color: null,
name: "The OG OG Komaeda",
},
user_identity: null,
tags: {
category_tags: [],
content_warnings: [],
Expand Down
5 changes: 4 additions & 1 deletion test/data/threads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
REVOLVER_OCELOT_POST,
} from "./posts";
import { GORE_BOARD_ID, RESTRICTED_BOARD_ID } from "./boards";
import { Thread, ThreadSummary } from "../../types/rest/threads";
import { Thread, ThreadSummary } from "types/open-api/generated/types";

import { GenericResponse } from "../../types/rest/responses";
import { TWISTED_MINDS_REALM_EXTERNAL_ID } from "./realms";
Expand Down Expand Up @@ -41,6 +41,7 @@ export const EXCELLENT_THREAD_SUMMARY: ThreadSummary = {
accessory: null,
color: "red",
},
user_identity: null,
own: false,
friend: false,
new: false,
Expand Down Expand Up @@ -105,6 +106,7 @@ export const FAVORITE_MURDER_THREAD_SUMMARY: ThreadSummary = {
accessory: null,
color: null,
},
user_identity: null,
new: false,
own: false,
friend: false,
Expand Down Expand Up @@ -182,6 +184,7 @@ export const RESTRICTED_THREAD: Thread = {
color: null,
name: "The OG OG Komaeda",
},
user_identity: null,
},
],
},
Expand Down
7 changes: 4 additions & 3 deletions types/open-api/comment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ components:
format: quill-delta
secret_identity:
description: The public-facing identity associated with the comment.
oneOf:
- $ref: "#/components/schemas/SecretIdentity"
- type: "null"
$ref: "#/components/schemas/SecretIdentity"
user_identity:
description: The identity of the original poster, if visible to the requester.
oneOf:
Expand All @@ -45,8 +43,11 @@ components:
required:
- id
- parent_post_id
- parent_comment_id
- chain_parent_id
- content
- secret_identity
- user_identity
- created_at
- new
- own
Expand Down
5 changes: 4 additions & 1 deletion types/open-api/contribution.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ components:
$ref: "#/components/schemas/SecretIdentity"
user_identity:
description: The identity of the original poster, if visible to the requester.
$ref: "#/components/schemas/Identity"
oneOf:
- $ref: "#/components/schemas/Identity"
- type: "null"
new:
type: boolean
own:
Expand All @@ -48,6 +50,7 @@ components:
- content
- created_at
- secret_identity
- user_identity
- friend
- own
- new
Expand Down
24 changes: 11 additions & 13 deletions types/open-api/generated/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,20 @@ const SecretIdentity = z.object({
accessory: z.union([z.string(), z.null()]).optional(),
});
const Identity = z.object({ name: z.string(), avatar: z.string() });
const Tags = z
.object({
whisper_tags: z.array(z.string()),
index_tags: z.array(z.string()),
category_tags: z.array(z.string()),
content_warnings: z.array(z.string()),
})
.partial();
const Tags = z.object({
whisper_tags: z.array(z.string()),
index_tags: z.array(z.string()),
category_tags: z.array(z.string()),
content_warnings: z.array(z.string()),
});
const Contribution = z.object({
id: z.string().uuid(),
parent_thread_id: z.string().uuid(),
parent_post_id: z.union([z.string(), z.null()]),
content: z.string(),
created_at: z.string(),
secret_identity: SecretIdentity,
user_identity: Identity.optional(),
user_identity: z.union([Identity, z.null()]),
new: z.boolean(),
own: z.boolean(),
friend: z.boolean(),
Expand All @@ -110,11 +108,11 @@ const Contribution = z.object({
const Comment = z.object({
id: z.string().uuid(),
parent_post_id: z.string().uuid(),
parent_comment_id: z.union([z.string(), z.null()]).optional(),
chain_parent_id: z.union([z.string(), z.null()]).optional(),
parent_comment_id: z.union([z.string(), z.null()]),
chain_parent_id: z.union([z.string(), z.null()]),
content: z.string(),
secret_identity: z.union([SecretIdentity, z.null()]),
user_identity: z.union([Identity, z.null()]).optional(),
secret_identity: SecretIdentity,
user_identity: z.union([Identity, z.null()]),
created_at: z.string(),
own: z.boolean(),
new: z.boolean(),
Expand Down
9 changes: 9 additions & 0 deletions types/open-api/generated/types.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,13 @@ import { z } from "zod";

{{#each schemas}}
export type {{@key}} = z.infer<typeof schemas.{{@key}}Schema>;
{{/each}}

// TODO: figure out how to make these endpoints camel case
{{#each endpoints}}
{{#if @root.options.withAlias}}
{{#if alias}}
export type {{alias}}Response = z.infer<typeof schemas.endpoints.{{alias}}.response>;
{{/if}}
{{/if}}
{{/each}}
119 changes: 119 additions & 0 deletions types/open-api/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,122 @@ export type UserSettings = z.infer<typeof schemas.UserSettingsSchema>;
export type updateUserSettings_Body = z.infer<
typeof schemas.updateUserSettings_BodySchema
>;

// TODO: figure out how to make these endpoints camel case
export type getBoardsByExternalIdResponse = z.infer<
typeof schemas.endpoints.getBoardsByExternalId.response
>;
export type createThreadResponse = z.infer<
typeof schemas.endpoints.createThread.response
>;
export type patchBoardsByExternalIdResponse = z.infer<
typeof schemas.endpoints.patchBoardsByExternalId.response
>;
export type mutesBoardsByExternalIdResponse = z.infer<
typeof schemas.endpoints.mutesBoardsByExternalId.response
>;
export type unmutesBoardsByExternalIdResponse = z.infer<
typeof schemas.endpoints.unmutesBoardsByExternalId.response
>;
export type dismissBoardsByExternalIdResponse = z.infer<
typeof schemas.endpoints.dismissBoardsByExternalId.response
>;
export type pinsBoardsByExternalIdResponse = z.infer<
typeof schemas.endpoints.pinsBoardsByExternalId.response
>;
export type unpinsBoardsByExternalIdResponse = z.infer<
typeof schemas.endpoints.unpinsBoardsByExternalId.response
>;
export type visitsBoardsByExternalIdResponse = z.infer<
typeof schemas.endpoints.visitsBoardsByExternalId.response
>;
export type getBoardsFeedByExternalIdResponse = z.infer<
typeof schemas.endpoints.getBoardsFeedByExternalId.response
>;
export type getPersonalFeedResponse = z.infer<
typeof schemas.endpoints.getPersonalFeed.response
>;
export type getUserStarFeedResponse = z.infer<
typeof schemas.endpoints.getUserStarFeed.response
>;
export type postCommentResponse = z.infer<
typeof schemas.endpoints.postComment.response
>;
export type postContributionResponse = z.infer<
typeof schemas.endpoints.postContribution.response
>;
export type editContributionResponse = z.infer<
typeof schemas.endpoints.editContribution.response
>;
export type getRealmsActivityByExternalIdResponse = z.infer<
typeof schemas.endpoints.getRealmsActivityByExternalId.response
>;
export type getInvitesByRealmIdResponse = z.infer<
typeof schemas.endpoints.getInvitesByRealmId.response
>;
export type createInviteByRealmIdResponse = z.infer<
typeof schemas.endpoints.createInviteByRealmId.response
>;
export type getInviteByNonceResponse = z.infer<
typeof schemas.endpoints.getInviteByNonce.response
>;
export type acceptInviteByNonceResponse = z.infer<
typeof schemas.endpoints.acceptInviteByNonce.response
>;
export type getCurrentUserNotificationsResponse = z.infer<
typeof schemas.endpoints.getCurrentUserNotifications.response
>;
export type dismissUserNotificationsResponse = z.infer<
typeof schemas.endpoints.dismissUserNotifications.response
>;
export type getRealmsBySlugResponse = z.infer<
typeof schemas.endpoints.getRealmsBySlug.response
>;
export type getSubscriptionResponse = z.infer<
typeof schemas.endpoints.getSubscription.response
>;
export type getThreadByExternalIdResponse = z.infer<
typeof schemas.endpoints.getThreadByExternalId.response
>;
export type updateThreadExternalIdResponse = z.infer<
typeof schemas.endpoints.updateThreadExternalId.response
>;
export type hideThreadByExternalIdResponse = z.infer<
typeof schemas.endpoints.hideThreadByExternalId.response
>;
export type unhideThreadByExternalIdResponse = z.infer<
typeof schemas.endpoints.unhideThreadByExternalId.response
>;
export type muteThreadByExternalIdResponse = z.infer<
typeof schemas.endpoints.muteThreadByExternalId.response
>;
export type unmuteThreadByExternalIdResponse = z.infer<
typeof schemas.endpoints.unmuteThreadByExternalId.response
>;
export type starThreadByExternalIdResponse = z.infer<
typeof schemas.endpoints.starThreadByExternalId.response
>;
export type unstarThreadByExternalIdResponse = z.infer<
typeof schemas.endpoints.unstarThreadByExternalId.response
>;
export type visitThreadByExternalIdResponse = z.infer<
typeof schemas.endpoints.visitThreadByExternalId.response
>;
export type getCurrentUserResponse = z.infer<
typeof schemas.endpoints.getCurrentUser.response
>;
export type updateCurrentUserResponse = z.infer<
typeof schemas.endpoints.updateCurrentUser.response
>;
export type getCurrentUserBobadexResponse = z.infer<
typeof schemas.endpoints.getCurrentUserBobadex.response
>;
export type getCurrentUserPinnedBoardsForRealmResponse = z.infer<
typeof schemas.endpoints.getCurrentUserPinnedBoardsForRealm.response
>;
export type getUserSettingsResponse = z.infer<
typeof schemas.endpoints.getUserSettings.response
>;
export type updateUserSettingsResponse = z.infer<
typeof schemas.endpoints.updateUserSettings.response
>;
5 changes: 5 additions & 0 deletions types/open-api/tags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ components:
type: array
items:
type: string
required:
- whisper_tags
- index_tags
- category_tags
- content_warnings
Loading
Loading