From 63f191aa95f6b5e280e43308e169d16c808cd5e8 Mon Sep 17 00:00:00 2001 From: Marcel <65048232+dromzeh@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:12:30 +0000 Subject: [PATCH] category routes impl --- src/v2/routes/category/all-categories.ts | 48 +++++++++ src/v2/routes/category/create-category.ts | 108 +++++++++++++++++++ src/v2/routes/category/delete-category.ts | 91 ++++++++++++++++ src/v2/routes/category/get-category.ts | 78 ++++++++++++++ src/v2/routes/category/handler.ts | 16 +++ src/v2/routes/category/modify-category.ts | 120 ++++++++++++++++++++++ src/v2/routes/category/view-category.ts | 0 src/v2/routes/handler.ts | 2 + 8 files changed, 463 insertions(+) create mode 100644 src/v2/routes/category/get-category.ts create mode 100644 src/v2/routes/category/handler.ts delete mode 100644 src/v2/routes/category/view-category.ts diff --git a/src/v2/routes/category/all-categories.ts b/src/v2/routes/category/all-categories.ts index e69de29b..82985681 100644 --- a/src/v2/routes/category/all-categories.ts +++ b/src/v2/routes/category/all-categories.ts @@ -0,0 +1,48 @@ +import { AppHandler } from "../handler" +import { getConnection } from "@/v2/db/turso" +import { assetCategory } from "@/v2/db/schema" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { createRoute } from "@hono/zod-openapi" +import { z } from "@hono/zod-openapi" +import { selectAssetCategorySchema } from "@/v2/db/schema" + +export const getAllCategoriesResponseSchema = z.object({ + success: z.literal(true), + categories: selectAssetCategorySchema.array(), +}) + +const getAllCategoriesRoute = createRoute({ + path: "/all", + method: "get", + summary: "Get all categories", + description: "Get all categories.", + tags: ["Category"], + responses: { + 200: { + description: "All categories.", + content: { + "application/json": { + schema: getAllCategoriesResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +export const AllCategoriesRoute = (handler: AppHandler) => { + handler.openapi(getAllCategoriesRoute, async (ctx) => { + const { drizzle } = await getConnection(ctx.env) + + const assetCategories = + (await drizzle.select().from(assetCategory)) ?? [] + + return ctx.json( + { + success: true, + categories: assetCategories, + }, + 200 + ) + }) +} diff --git a/src/v2/routes/category/create-category.ts b/src/v2/routes/category/create-category.ts index e69de29b..cad0c0d5 100644 --- a/src/v2/routes/category/create-category.ts +++ b/src/v2/routes/category/create-category.ts @@ -0,0 +1,108 @@ +import { AppHandler } from "../handler" +import { assetCategory } from "@/v2/db/schema" +import { eq } from "drizzle-orm" +import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" +import { getConnection } from "@/v2/db/turso" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" +import { selectAssetCategorySchema } from "@/v2/db/schema" + +export const createAssetCategorySchema = z.object({ + name: z.string().min(3).max(32).openapi({ + description: "The name of the asset category.", + example: "splash-art", + }), + formattedName: z.string().min(3).max(64).openapi({ + description: "The formatted name of the category.", + example: "Splash Art", + }), +}) + +export const createAssetCategoryResponseSchema = z.object({ + success: z.literal(true), + assetCategory: selectAssetCategorySchema, +}) + +const createAssetCategoryRoute = createRoute({ + path: "/create", + method: "post", + summary: "Create a category", + description: "Create a new category.", + tags: ["Category"], + request: { + body: { + content: { + "application/json": { + schema: createAssetCategorySchema, + }, + }, + }, + }, + responses: { + 200: { + description: "Returns the new category.", + content: { + "application/json": { + schema: createAssetCategoryResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +export const CreateCategoryRoute = (handler: AppHandler) => { + handler.openapi(createAssetCategoryRoute, async (ctx) => { + const authSessionManager = new AuthSessionManager(ctx) + + const { user } = await authSessionManager.validateSession() + + if (!user || user.role != "creator") { + return ctx.json( + { + success: false, + message: "Unauthorized", + }, + 401 + ) + } + + const { name, formattedName } = ctx.req.valid("json") + + const { drizzle } = getConnection(ctx.env) + + const [categoryExists] = await drizzle + .select({ name: assetCategory.name }) + .from(assetCategory) + .where(eq(assetCategory.name, name)) + + if (categoryExists) { + return ctx.json( + { + success: false, + message: "Category already exists", + }, + 400 + ) + } + + const [newCategory] = await drizzle + .insert(assetCategory) + .values({ + id: name, + name, + formattedName, + lastUpdated: new Date().toISOString(), + }) + .returning() + + return ctx.json( + { + success: true, + assetCategory: newCategory, + }, + 200 + ) + }) +} diff --git a/src/v2/routes/category/delete-category.ts b/src/v2/routes/category/delete-category.ts index e69de29b..469e4bd9 100644 --- a/src/v2/routes/category/delete-category.ts +++ b/src/v2/routes/category/delete-category.ts @@ -0,0 +1,91 @@ +import { AppHandler } from "../handler" +import { getConnection } from "@/v2/db/turso" +import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" +import { assetCategory } from "@/v2/db/schema" +import { eq } from "drizzle-orm" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +export const deleteAssetCategorySchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + description: "The ID of the category to delete.", + example: "splash-art", + required: true, + }, + }), +}) + +export const deleteAssetCategoryResponseSchema = z.object({ + success: z.literal(true), +}) + +const deleteCategoryRoute = createRoute({ + path: "/{id}/delete", + method: "delete", + summary: "Delete a category", + description: "Delete a category.", + tags: ["Category"], + request: { + params: deleteAssetCategorySchema, + }, + responses: { + 200: { + description: "Returns boolean indicating success.", + content: { + "application/json": { + schema: deleteAssetCategoryResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +export const DeleteAssetCategoryRoute = (handler: AppHandler) => { + handler.openapi(deleteCategoryRoute, async (ctx) => { + const id = ctx.req.valid("param").id + + const { drizzle } = await getConnection(ctx.env) + + const [foundCategory] = await drizzle + .select({ id: assetCategory.id }) + .from(assetCategory) + .where(eq(assetCategory.id, id)) + + if (!foundCategory) { + return ctx.json( + { + success: false, + message: "Category not found", + }, + 404 + ) + } + + const authSessionManager = new AuthSessionManager(ctx) + const { user } = await authSessionManager.validateSession() + + if (!user || user.role != "creator") { + return ctx.json( + { + success: false, + message: "Unauthorized", + }, + 401 + ) + } + + await drizzle.delete(assetCategory).where(eq(assetCategory.id, id)) + + return ctx.json( + { + success: true, + }, + 200 + ) + }) +} diff --git a/src/v2/routes/category/get-category.ts b/src/v2/routes/category/get-category.ts new file mode 100644 index 00000000..c5c839ef --- /dev/null +++ b/src/v2/routes/category/get-category.ts @@ -0,0 +1,78 @@ +import { AppHandler } from "../handler" +import { createRoute } from "@hono/zod-openapi" +import { assetCategory } from "@/v2/db/schema" +import { getConnection } from "@/v2/db/turso" +import { eq } from "drizzle-orm" +import { z } from "@hono/zod-openapi" +import { selectAssetCategorySchema } from "@/v2/db/schema" +import { GenericResponses } from "@/v2/lib/response-schemas" + +const getAssetCategoryByIdSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + in: "path", + description: "The ID of the category to retrieve.", + example: "splash-art", + required: true, + }, + }), +}) + +const getAssetCategoryByIdResponseSchema = z.object({ + success: z.literal(true), + category: selectAssetCategorySchema, +}) + +const getAssetCategoryByIdRoute = createRoute({ + path: "/{id}", + method: "get", + summary: "Get a category", + description: "Get a category by their ID.", + tags: ["Category"], + request: { + params: getAssetCategoryByIdSchema, + }, + responses: { + 200: { + description: "Category was found.", + content: { + "application/json": { + schema: getAssetCategoryByIdResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +export const GetCategoryByIdRoute = (handler: AppHandler) => { + handler.openapi(getAssetCategoryByIdRoute, async (ctx) => { + const id = ctx.req.valid("param").id + + const { drizzle } = await getConnection(ctx.env) + + const [foundCategory] = await drizzle + .select() + .from(assetCategory) + .where(eq(assetCategory.id, id)) + + if (!foundCategory) { + return ctx.json( + { + success: false, + message: "Category not found", + }, + 400 + ) + } + + return ctx.json( + { + success: true, + category: foundCategory, + }, + 200 + ) + }) +} diff --git a/src/v2/routes/category/handler.ts b/src/v2/routes/category/handler.ts new file mode 100644 index 00000000..909ff67d --- /dev/null +++ b/src/v2/routes/category/handler.ts @@ -0,0 +1,16 @@ +import { OpenAPIHono } from "@hono/zod-openapi" +import { AllCategoriesRoute } from "./all-categories" +import { CreateCategoryRoute } from "./create-category" +import { GetCategoryByIdRoute } from "./get-category" +import { ModifyAssetCategoryRoute } from "./modify-category" +import { DeleteAssetCategoryRoute } from "./delete-category" + +const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() + +AllCategoriesRoute(handler) +GetCategoryByIdRoute(handler) +CreateCategoryRoute(handler) +ModifyAssetCategoryRoute(handler) +DeleteAssetCategoryRoute(handler) + +export default handler diff --git a/src/v2/routes/category/modify-category.ts b/src/v2/routes/category/modify-category.ts index e69de29b..c696e404 100644 --- a/src/v2/routes/category/modify-category.ts +++ b/src/v2/routes/category/modify-category.ts @@ -0,0 +1,120 @@ +import { AppHandler } from "../handler" +import { eq } from "drizzle-orm" +import { AuthSessionManager } from "@/v2/lib/managers/auth/user-session-manager" +import { getConnection } from "@/v2/db/turso" +import { assetCategory, selectAssetCategorySchema } from "@/v2/db/schema" +import { createRoute } from "@hono/zod-openapi" +import { GenericResponses } from "@/v2/lib/response-schemas" +import { z } from "@hono/zod-openapi" + +export const modifyAssetCategoryPathSchema = z.object({ + id: z.string().openapi({ + param: { + name: "id", + description: "The id of the category to modify.", + example: "splash-art", + in: "path", + required: true, + }, + }), +}) + +const modifyAssetCategorySchema = z.object({ + name: z.string().min(3).max(32).openapi({ + description: "The new name of the category.", + example: "splash-art", + }), + formattedName: z.string().min(3).max(64).openapi({ + description: "The new formatted name of the category.", + example: "Splash Art", + }), +}) + +const modifyAssetCategoryResponseSchema = z.object({ + success: z.literal(true), + assetCategory: selectAssetCategorySchema, +}) + +export const modifyAssetCategoryRoute = createRoute({ + path: "/{id}/modify", + method: "patch", + summary: "Modify a category", + description: "Modify an existing category.", + tags: ["Category"], + request: { + params: modifyAssetCategoryPathSchema, + body: { + content: { + "application/json": { + schema: modifyAssetCategorySchema, + }, + }, + }, + }, + responses: { + 200: { + description: "Returns the new category attributes", + content: { + "application/json": { + schema: modifyAssetCategoryResponseSchema, + }, + }, + }, + ...GenericResponses, + }, +}) + +export const ModifyAssetCategoryRoute = (handler: AppHandler) => { + handler.openapi(modifyAssetCategoryRoute, async (ctx) => { + const authSessionManager = new AuthSessionManager(ctx) + const { user } = await authSessionManager.validateSession() + + if (!user || user.role != "creator") { + return ctx.json( + { + success: false, + message: "Unauthorized", + }, + 401 + ) + } + + const { name, formattedName } = ctx.req.valid("json") + const { id } = ctx.req.valid("param") + + const { drizzle } = getConnection(ctx.env) + + const [existingCategory] = await drizzle + .select({ id: assetCategory.id }) + .from(assetCategory) + .where(eq(assetCategory.id, id)) + + if (!existingCategory) { + return ctx.json( + { + success: false, + message: "Category not found", + }, + 404 + ) + } + + const [updatedCategory] = await drizzle + .update(assetCategory) + .set({ + name, + formattedName, + lastUpdated: new Date().toISOString(), + }) + .where(eq(assetCategory.id, id)) + .returning() + + return ctx.json( + { + success: true, + assetCategory: updatedCategory, + }, + 200 + ) + }) +} diff --git a/src/v2/routes/category/view-category.ts b/src/v2/routes/category/view-category.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src/v2/routes/handler.ts b/src/v2/routes/handler.ts index 194f9d6e..03d569cb 100644 --- a/src/v2/routes/handler.ts +++ b/src/v2/routes/handler.ts @@ -6,11 +6,13 @@ import TagRoute from "@/v2/routes/tags/handler" import ContributorRoute from "@/v2/routes/contributors/handler" import AuthRoute from "@/v2/routes/auth/handler" import RequestFormRoute from "@/v2/routes/requests/handler" +import CategoriesRoute from "@/v2/routes/category/handler" const handler = new OpenAPIHono<{ Bindings: Bindings; Variables: Variables }>() handler.route("/game", GameRoute) handler.route("/asset", AssetRoute) +handler.route("/category", CategoriesRoute) handler.route("/tags", TagRoute) handler.route("/user", UserRoute) handler.route("/contributors", ContributorRoute)