From 199758150d5c04f77f111fc72b8bc982298da6c1 Mon Sep 17 00:00:00 2001 From: sianida26 Date: Wed, 8 May 2024 00:25:41 +0700 Subject: [PATCH] Added zValidator wrapper for returning default error response --- apps/backend/src/routes/users/route.ts | 155 ++++++++------------- apps/backend/src/utils/requestValidator.ts | 43 ++++++ 2 files changed, 103 insertions(+), 95 deletions(-) create mode 100644 apps/backend/src/utils/requestValidator.ts diff --git a/apps/backend/src/routes/users/route.ts b/apps/backend/src/routes/users/route.ts index d30b5a2..6b40246 100644 --- a/apps/backend/src/routes/users/route.ts +++ b/apps/backend/src/routes/users/route.ts @@ -10,6 +10,7 @@ import { hashPassword } from "../../utils/passwordUtils"; import { rolesToUsers } from "../../drizzle/schema/rolesToUsers"; import { rolesSchema } from "../../drizzle/schema/roles"; import HonoEnv from "../../types/HonoEnv"; +import requestValidator from "../../utils/requestValidator"; const userFormSchema = z.object({ name: z.string().min(1).max(255), @@ -54,7 +55,7 @@ const usersRoute = new Hono() }) .get( "/", - zValidator( + requestValidator( "query", z.object({ includeTrashed: z.string().default("false"), @@ -86,7 +87,7 @@ const usersRoute = new Hono() //get user by id .get( "/:id", - zValidator( + requestValidator( "query", z.object({ includeTrashed: z.string().default("false"), @@ -144,111 +145,75 @@ const usersRoute = new Hono() } ) //create user - .post( - "/", - zValidator("form", userFormSchema, (result) => { - if (!result.success) { - let errors = result.error.flatten().fieldErrors as Record< - string, - string[] - >; - let firstErrors: Record = {}; - for (let field in errors) { - firstErrors[field] = errors[field][0]; // Grabbing the first error message for each field - } - throw new HTTPException(422, { - message: JSON.stringify(firstErrors), - }); + .post("/", requestValidator("form", userFormSchema), async (c) => { + const userData = c.req.valid("form"); + + const user = await db + .insert(users) + .values({ + name: userData.name, + username: userData.username, + email: userData.email, + password: await hashPassword(userData.password), + isEnabled: userData.isEnabled.toLowerCase() === "true", + }) + .returning(); + + if (userData.roles) { + const roles = JSON.parse(userData.roles) as string[]; + console.log(roles); + + if (roles.length) { + await db.insert(rolesToUsers).values( + roles.map((role) => ({ + userId: user[0].id, + roleId: role, + })) + ); } - }), - async (c) => { - const userData = c.req.valid("form"); - - const user = await db - .insert(users) - .values({ - name: userData.name, - username: userData.username, - email: userData.email, - password: await hashPassword(userData.password), - isEnabled: userData.isEnabled.toLowerCase() === "true", - }) - .returning(); - - if (userData.roles) { - const roles = JSON.parse(userData.roles) as string[]; - console.log(roles); - - if (roles.length) { - await db.insert(rolesToUsers).values( - roles.map((role) => ({ - userId: user[0].id, - roleId: role, - })) - ); - } - } - - return c.json( - { - message: "User created successfully", - }, - 201 - ); } - ) + + return c.json( + { + message: "User created successfully", + }, + 201 + ); + }) //update user - .patch( - "/:id", - zValidator("form", userUpdateSchema, (result) => { - if (!result.success) { - let errors = result.error.flatten().fieldErrors as Record< - string, - string[] - >; - let firstErrors: Record = {}; - for (let field in errors) { - firstErrors[field] = errors[field][0]; // Grabbing the first error message for each field - } - throw new HTTPException(422, { - message: JSON.stringify(firstErrors), - }); - } - }), - async (c) => { - const userId = c.req.param("id"); - const userData = c.req.valid("form"); + .patch("/:id", requestValidator("form", userUpdateSchema), async (c) => { + const userId = c.req.param("id"); + const userData = c.req.valid("form"); - const user = await db - .select() - .from(users) - .where(and(eq(users.id, userId), isNull(users.deletedAt))); + const user = await db + .select() + .from(users) + .where(and(eq(users.id, userId), isNull(users.deletedAt))); - if (!user[0]) return c.notFound(); + if (!user[0]) return c.notFound(); - await db - .update(users) - .set({ - ...userData, - ...(userData.password - ? { password: await hashPassword(userData.password) } - : {}), - updatedAt: new Date(), - isEnabled: userData.isEnabled.toLowerCase() === "true", - }) - .where(eq(users.id, userId)); + await db + .update(users) + .set({ + ...userData, + ...(userData.password + ? { password: await hashPassword(userData.password) } + : {}), + updatedAt: new Date(), + isEnabled: userData.isEnabled.toLowerCase() === "true", + }) + .where(eq(users.id, userId)); - return c.json({ - message: "User updated successfully", - }); - } - ) + return c.json({ + message: "User updated successfully", + }); + }) //delete user .delete( "/:id", - zValidator( + requestValidator( "form", z.object({ skipTrash: z.string().default("false"), diff --git a/apps/backend/src/utils/requestValidator.ts b/apps/backend/src/utils/requestValidator.ts new file mode 100644 index 0000000..4e2b4fd --- /dev/null +++ b/apps/backend/src/utils/requestValidator.ts @@ -0,0 +1,43 @@ +import { zValidator } from "@hono/zod-validator"; +import DashboardError from "../errors/DashboardError"; + +type ValidatorParameters = Parameters; + +/** + * Creates a request validator using the Zod schema. + * This middleware function is designed for use with the Hono framework to validate incoming requests. + * If the validation fails, it throws a `DashboardError` with detailed information about the validation errors. + * + * @param parameters - Parameters expected by `zValidator`. The first parameter is the Zod schema, + * the second is options for the Zod validator, and the third is a custom error handler. + * @returns A middleware function that validates the request against the provided schema. + */ +const requestValidator = (...parameters: ValidatorParameters) => { + return zValidator( + parameters[0], + parameters[1], + parameters[2] ?? + ((result) => { + if (!result.success) { + let errors = result.error.flatten().fieldErrors as Record< + string, + string[] + >; + let firstErrors: Record = {}; + for (let field in errors) { + firstErrors[field] = errors[field][0]; // Grabbing the first error message for each field + } + throw new DashboardError({ + errorCode: "INVALID_FORM_DATA", + message: + "Validation failed. Please check your input and try again.", + formErrors: firstErrors, + severity: "LOW", + statusCode: 422, + }); + } + }) + ); +}; + +export default requestValidator;