From 0b045823d73777cfaa93112fdc9c09b97698bc3a Mon Sep 17 00:00:00 2001 From: Gabriel Machin Date: Mon, 15 Jan 2024 14:56:15 -0300 Subject: [PATCH 1/3] feat: Add body validation --- src/controllers/auth.ts | 5 +++++ src/middlewares/error.ts | 8 +++++++- src/utils/validator.ts | 11 +++++++++++ src/validator/auth.ts | 35 +++++++++++++++++++++++++++++++++++ tsconfig.json | 1 + tsoa.json | 1 + 6 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/utils/validator.ts create mode 100644 src/validator/auth.ts diff --git a/src/controllers/auth.ts b/src/controllers/auth.ts index 0a75a7a..935041d 100644 --- a/src/controllers/auth.ts +++ b/src/controllers/auth.ts @@ -8,11 +8,14 @@ import { AuthenticatedRequest, LoginParams, } from 'types'; +import { validateLoginSchema, validateUserSchema } from 'validator/auth'; @Route('v1/auth') export class AuthControllerV1 extends Controller { @Post('/register') public async register(@Body() user: CreateUserParams): Promise { + validateUserSchema(user); + const authReturn = await AuthService.register(user); this.setStatus(httpStatus.CREATED); return authReturn; @@ -20,6 +23,8 @@ export class AuthControllerV1 extends Controller { @Post('/login') public async login(@Body() loginParams: LoginParams): Promise { + validateLoginSchema(loginParams); + const authReturn = await AuthService.login(loginParams); this.setStatus(httpStatus.OK); return authReturn; diff --git a/src/middlewares/error.ts b/src/middlewares/error.ts index 37552d3..33dc1be 100644 --- a/src/middlewares/error.ts +++ b/src/middlewares/error.ts @@ -21,7 +21,13 @@ export const errorConverter = ( next(error); }; -export const errorHandler = (err: ApiError, req: Request, res: Response) => { +export const errorHandler = ( + err: ApiError, + req: Request, + res: Response, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + next: NextFunction, +) => { let { httpCode, message } = err; if (!err.isOperational) { httpCode = diff --git a/src/utils/validator.ts b/src/utils/validator.ts new file mode 100644 index 0000000..fee471c --- /dev/null +++ b/src/utils/validator.ts @@ -0,0 +1,11 @@ +import { ZodError } from 'zod'; + +export const formatZodError = (zodError: ZodError) => { + const formattedError = zodError.errors.map((error) => { + return { + field: error.path, + error: error.message, + }; + }); + return formattedError; +}; diff --git a/src/validator/auth.ts b/src/validator/auth.ts new file mode 100644 index 0000000..2f0521a --- /dev/null +++ b/src/validator/auth.ts @@ -0,0 +1,35 @@ +import { ZodError, z } from 'zod'; + +import { CreateUserParams, LoginParams } from 'types'; +import { ApiError } from 'utils/apiError'; +import { errors } from 'config/errors'; +import { formatZodError } from 'utils/validator'; + +const userSchema = z.object({ + email: z.string().email({ message: 'Invalid email' }), + name: z.string(), + password: z.string().min(1, { message: "Can't be an empty password" }), +}); + +const loginSchema = z.object({ + email: z.string().email({ message: 'Invalid email' }), + password: z.string().min(1, { message: "Can't be an empty password" }), +}); + +export const validateUserSchema = (user: CreateUserParams) => { + try { + userSchema.parse(user); + } catch (err) { + const formattedZodError = formatZodError(err as ZodError); + throw new ApiError(errors.VALIDATION_ERROR, true, formattedZodError); + } +}; + +export const validateLoginSchema = (loginParams: LoginParams) => { + try { + loginSchema.parse(loginParams); + } catch (err) { + const formattedZodError = formatZodError(err as ZodError); + throw new ApiError(errors.VALIDATION_ERROR, true, formattedZodError); + } +}; diff --git a/tsconfig.json b/tsconfig.json index 8e5b07a..2de15e8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ "tests/*": ["tests/*"], "types/*": ["types/*"], "utils/*": ["utils/*"], + "validator/*": ["validator/*"], "root/*": ["../*"] } } diff --git a/tsoa.json b/tsoa.json index b48521b..b6c26c6 100644 --- a/tsoa.json +++ b/tsoa.json @@ -28,6 +28,7 @@ "tests/*": ["tests/*"], "types/*": ["types/*"], "utils/*": ["utils/*"], + "validator/*": ["validator/*"], "root/*": ["../*"] } } From 72dbc728fb2768ff00b3fd0c0bf2e97a64dbcb85 Mon Sep 17 00:00:00 2001 From: Gabriel Machin Date: Wed, 17 Jan 2024 14:46:12 -0300 Subject: [PATCH 2/3] Fix comments --- src/controllers/auth.ts | 2 +- src/{validator => data-schemas}/auth.ts | 18 ++++++++++++++---- tsconfig.json | 2 +- tsoa.json | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) rename src/{validator => data-schemas}/auth.ts (68%) diff --git a/src/controllers/auth.ts b/src/controllers/auth.ts index 935041d..e3f3eef 100644 --- a/src/controllers/auth.ts +++ b/src/controllers/auth.ts @@ -8,7 +8,7 @@ import { AuthenticatedRequest, LoginParams, } from 'types'; -import { validateLoginSchema, validateUserSchema } from 'validator/auth'; +import { validateLoginSchema, validateUserSchema } from 'data-schemas/auth'; @Route('v1/auth') export class AuthControllerV1 extends Controller { diff --git a/src/validator/auth.ts b/src/data-schemas/auth.ts similarity index 68% rename from src/validator/auth.ts rename to src/data-schemas/auth.ts index 2f0521a..38eafd9 100644 --- a/src/validator/auth.ts +++ b/src/data-schemas/auth.ts @@ -5,20 +5,30 @@ import { ApiError } from 'utils/apiError'; import { errors } from 'config/errors'; import { formatZodError } from 'utils/validator'; -const userSchema = z.object({ +const passwordLength = 10; + +const userCreationSchema = z.object({ email: z.string().email({ message: 'Invalid email' }), name: z.string(), - password: z.string().min(1, { message: "Can't be an empty password" }), + password: z + .string() + .min(passwordLength, { message: "Can't be an empty password" }), }); +export type UserCreationSchema = z.infer; + const loginSchema = z.object({ email: z.string().email({ message: 'Invalid email' }), - password: z.string().min(1, { message: "Can't be an empty password" }), + password: z + .string() + .min(passwordLength, { message: "Can't be an empty password" }), }); +export type UserLoginSchema = z.infer; + export const validateUserSchema = (user: CreateUserParams) => { try { - userSchema.parse(user); + userCreationSchema.parse(user); } catch (err) { const formattedZodError = formatZodError(err as ZodError); throw new ApiError(errors.VALIDATION_ERROR, true, formattedZodError); diff --git a/tsconfig.json b/tsconfig.json index 2de15e8..4ecccac 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,7 +23,7 @@ "tests/*": ["tests/*"], "types/*": ["types/*"], "utils/*": ["utils/*"], - "validator/*": ["validator/*"], + "data-schemas/*": ["data-schemas/*"], "root/*": ["../*"] } } diff --git a/tsoa.json b/tsoa.json index b6c26c6..5377a80 100644 --- a/tsoa.json +++ b/tsoa.json @@ -28,7 +28,7 @@ "tests/*": ["tests/*"], "types/*": ["types/*"], "utils/*": ["utils/*"], - "validator/*": ["validator/*"], + "data-schemas/*": ["data-schemas/*"], "root/*": ["../*"] } } From f1395003ee84e185ec796b033af314dcd1eac368 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 26 Jan 2024 13:01:28 -0300 Subject: [PATCH 3/3] Fix comments --- src/data-schemas/auth.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data-schemas/auth.ts b/src/data-schemas/auth.ts index 38eafd9..0f8eed6 100644 --- a/src/data-schemas/auth.ts +++ b/src/data-schemas/auth.ts @@ -5,14 +5,14 @@ import { ApiError } from 'utils/apiError'; import { errors } from 'config/errors'; import { formatZodError } from 'utils/validator'; -const passwordLength = 10; +const MINIMUM_PASSWORD_LENGTH = 10; const userCreationSchema = z.object({ email: z.string().email({ message: 'Invalid email' }), name: z.string(), password: z .string() - .min(passwordLength, { message: "Can't be an empty password" }), + .min(MINIMUM_PASSWORD_LENGTH, { message: "Can't be an empty password" }), }); export type UserCreationSchema = z.infer; @@ -21,7 +21,7 @@ const loginSchema = z.object({ email: z.string().email({ message: 'Invalid email' }), password: z .string() - .min(passwordLength, { message: "Can't be an empty password" }), + .min(MINIMUM_PASSWORD_LENGTH, { message: "Can't be an empty password" }), }); export type UserLoginSchema = z.infer;