Skip to content

Commit

Permalink
chore: Define hoarder's rest API in zod format
Browse files Browse the repository at this point in the history
  • Loading branch information
MohamedBassem committed Oct 20, 2024
1 parent 4086c37 commit 3c1ec3a
Show file tree
Hide file tree
Showing 9 changed files with 726 additions and 5 deletions.
46 changes: 46 additions & 0 deletions packages/open-api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as fs from "fs";
import {
OpenApiGeneratorV3,
OpenAPIRegistry,
} from "@asteasolutions/zod-to-openapi";
import * as yaml from "yaml";

import { registry as bookmarksRegistry } from "./lib/bookmarks";
import { registry as commonRegistry } from "./lib/common";
import { registry as listsRegistry } from "./lib/lists";
import { registry as tagsRegistry } from "./lib/tags";

function getOpenApiDocumentation() {
const registry = new OpenAPIRegistry([
commonRegistry,
bookmarksRegistry,
listsRegistry,
tagsRegistry,
]);

const generator = new OpenApiGeneratorV3(registry.definitions);

return generator.generateDocument({
openapi: "3.0.0",
info: {
version: "1.0.0",
title: "Hoarder API",
description: "The API for the Hoarder app",
},
servers: [{ url: "v1" }],
});
}

function writeDocumentation() {
// OpenAPI JSON
const docs = getOpenApiDocumentation();

// YAML equivalent
const fileContent = yaml.stringify(docs);

fs.writeFileSync(`./openapi-spec.yml`, fileContent, {
encoding: "utf-8",
});
}

writeDocumentation();
212 changes: 212 additions & 0 deletions packages/open-api/lib/bookmarks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import {
extendZodWithOpenApi,
OpenAPIRegistry,
} from "@asteasolutions/zod-to-openapi";
import { z } from "zod";

import {
zBareBookmarkSchema,
zManipulatedTagSchema,
zNewBookmarkRequestSchema,
zUpdateBookmarksRequestSchema,
} from "@hoarder/shared/types/bookmarks";

import { BearerAuth } from "./common";
import {
BookmarkSchema,
PaginatedBookmarksSchema,
PaginationSchema,
} from "./pagination";
import { TagIdSchema } from "./tags";

export const registry = new OpenAPIRegistry();
extendZodWithOpenApi(z);

export const BookmarkIdSchema = registry.registerParameter(
"BookmarkId",
z.string().openapi({
param: {
name: "bookmarkId",
in: "path",
},
example: "ieidlxygmwj87oxz5hxttoc8",
}),
);

registry.registerPath({
method: "get",
path: "/bookmarks",
description: "Get all bookmarks",
summary: "Get all bookmarks",
security: [{ [BearerAuth.name]: [] }],
request: {
query: z
.object({
archived: z.boolean().optional(),
favourited: z.boolean().optional(),
})
.merge(PaginationSchema),
},
responses: {
200: {
description: "Object with all bookmarks data.",
content: {
"application/json": {
schema: PaginatedBookmarksSchema,
},
},
},
},
});

registry.registerPath({
method: "post",
path: "/bookmarks",
description: "Create a new bookmark",
summary: "Create a new bookmark",
security: [{ [BearerAuth.name]: [] }],
request: {
body: {
description: "The bookmark to create",
content: {
"application/json": {
schema: zNewBookmarkRequestSchema,
},
},
},
},
responses: {
201: {
description: "The created bookmark",
content: {
"application/json": {
schema: BookmarkSchema,
},
},
},
},
});
registry.registerPath({
method: "get",
path: "/bookmarks/{bookmarkId}",
description: "Get bookmark by its id",
summary: "Get a single bookmark",
security: [{ [BearerAuth.name]: [] }],
request: {
params: z.object({ bookmarkId: BookmarkIdSchema }),
},
responses: {
200: {
description: "Object with bookmark data.",
content: {
"application/json": {
schema: BookmarkSchema,
},
},
},
},
});

registry.registerPath({
method: "delete",
path: "/bookmarks/{bookmarkId}",
description: "Delete bookmark by its id",
summary: "Delete a bookmark",
security: [{ [BearerAuth.name]: [] }],
request: {
params: z.object({ bookmarkId: BookmarkIdSchema }),
},
responses: {
204: {
description: "No content - the bookmark was deleted",
},
},
});

registry.registerPath({
method: "patch",
path: "/bookmarks/{bookmarkId}",
description: "Update bookmark by its id",
summary: "Update a bookmark",
security: [{ [BearerAuth.name]: [] }],
request: {
params: z.object({ bookmarkId: BookmarkIdSchema }),
body: {
description:
"The data to update. Only the fields you want to update need to be provided.",
content: {
"application/json": {
schema: zUpdateBookmarksRequestSchema.omit({ bookmarkId: true }),
},
},
},
},
responses: {
200: {
description: "The updated bookmark",
content: {
"application/json": {
schema: zBareBookmarkSchema,
},
},
},
},
});

registry.registerPath({
method: "post",
path: "/bookmarks/{bookmarkId}/tags",
description: "Attach tags to a bookmark",
summary: "Attach tags to a bookmark",
security: [{ [BearerAuth.name]: [] }],
request: {
params: z.object({ bookmarkId: BookmarkIdSchema }),
body: {
description: "The tags to attach.",
content: {
"application/json": {
schema: z.object({ tags: z.array(zManipulatedTagSchema) }),
},
},
},
},
responses: {
200: {
description: "The list of attached tag ids",
content: {
"application/json": {
schema: z.object({ attached: z.array(TagIdSchema) }),
},
},
},
},
});

registry.registerPath({
method: "delete",
path: "/bookmarks/{bookmarkId}/tags",
description: "Detach tags from a bookmark",
summary: "Detach tags from a bookmark",
security: [{ [BearerAuth.name]: [] }],
request: {
params: z.object({ bookmarkId: BookmarkIdSchema }),
body: {
description: "The tags to detach.",
content: {
"application/json": {
schema: z.object({ tags: z.array(zManipulatedTagSchema) }),
},
},
},
},
responses: {
200: {
description: "The list of detached tag ids",
content: {
"application/json": {
schema: z.object({ detached: z.array(TagIdSchema) }),
},
},
},
},
});
12 changes: 12 additions & 0 deletions packages/open-api/lib/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { OpenAPIRegistry } from "@asteasolutions/zod-to-openapi";

export const registry = new OpenAPIRegistry();
export const BearerAuth = registry.registerComponent(
"securitySchemes",
"bearerAuth",
{
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
);
Loading

0 comments on commit 3c1ec3a

Please sign in to comment.