diff --git a/src/authentication/validation/user.auth.schema.validation.ts b/src/authentication/validation/user.auth.schema.validation.ts index 18a7fd5..9bc30c6 100644 --- a/src/authentication/validation/user.auth.schema.validation.ts +++ b/src/authentication/validation/user.auth.schema.validation.ts @@ -1,76 +1,172 @@ import * as Joi from '@hapi/joi'; +import { ExceptionMessage } from '../../../src/constants/exception.message'; export const UserPasswordSignupInputSchema = Joi.object({ - email: Joi.string().email({ tlds: { allow: false } }), - phone: Joi.number(), - password: Joi.string().required().min(10), + email: Joi.string() + .email({ tlds: { allow: false } }) + .messages({ + 'string.email': ExceptionMessage.INVALID_EMAIL_ERROR, + }), + phone: Joi.number().messages({ + 'number.base': ExceptionMessage.INVALID_PHONE_ERROR, + }), + password: Joi.string().required().min(10).label('Password').messages({ + 'string.min': ExceptionMessage.PASSWORD_MIN_CHAR_LENGTH_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), firstName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .required(), + .required() + .label('First name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), middleName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .allow('', null), + .allow('', null) + .label('Middle name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + }), lastName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .required(), + .required() + .label('Last name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }) .options({ abortEarly: false }) .or('email', 'phone'); export const UserOTPSignupInputSchema = Joi.object({ - email: Joi.string().email({ tlds: { allow: false } }), - phone: Joi.number().required(), + email: Joi.string() + .email({ tlds: { allow: false } }) + .messages({ + 'string.email': ExceptionMessage.INVALID_EMAIL_ERROR, + }), + phone: Joi.number().required().messages({ + 'number.base': ExceptionMessage.INVALID_PHONE_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), firstName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .required(), - middleName: Joi.string().regex(/^[a-zA-Z ]*$/), + .required() + .label('First name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), + middleName: Joi.string() + .regex(/^[a-zA-Z ]*$/) + .label('Middle name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + }), lastName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .required(), + .required() + .label('Last name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }).options({ abortEarly: false }); export const UserPasswordLoginInputSchema = Joi.object({ - username: Joi.string().required(), - password: Joi.string().min(10).required(), + username: Joi.string().required().label('Username').messages({ + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), + password: Joi.string().min(10).required().label('Password').messages({ + 'string.min': ExceptionMessage.PASSWORD_MIN_CHAR_LENGTH_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }).options({ abortEarly: false }); export const UserOTPLoginInputSchema = Joi.object({ - username: Joi.string().required(), - otp: Joi.string().required(), + username: Joi.string().required().label('Username').messages({ + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), + otp: Joi.string().required().label('OTP').messages({ + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }).options({ abortEarly: false }); export const UserSendOTPInputSchema = Joi.object({ - username: Joi.string().required(), + username: Joi.string().required().label('Username').messages({ + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }).options({ abortEarly: false }); export const UserPasswordInputSchema = Joi.object({ - currentPassword: Joi.string().required().min(10), + currentPassword: Joi.string() + .required() + .min(10) + .label('Current Password') + .messages({ + 'string.min': ExceptionMessage.PASSWORD_MIN_CHAR_LENGTH_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), newPassword: Joi.string() .disallow(Joi.ref('currentPassword')) .required() - .min(10), + .min(10) + .label('New Password') + .messages({ + 'any.invalid': ExceptionMessage.NEW_PASSWORD_MATCH_CURRENT_PASSWORD_ERROR, + 'string.min': ExceptionMessage.PASSWORD_MIN_CHAR_LENGTH_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }).options({ abortEarly: false }); export const GoogleUserSchema = Joi.object({ email: Joi.string() .email({ tlds: { allow: false } }) - .required(), + .required() + .label('Email') + .messages({ + 'string.email': ExceptionMessage.INVALID_EMAIL_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), firstName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .required(), - middleName: Joi.string().regex(/^[a-zA-Z ]*$/), + .required() + .label('First name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), + middleName: Joi.string() + .regex(/^[a-zA-Z ]*$/) + .label('Middle name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + }), lastName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .required(), - externalUserId: Joi.string().required(), + .required() + .label('Last name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), + externalUserId: Joi.string().required().label('External User Id').messages({ + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }).options({ abortEarly: false }); export const GenerateOtpInputSchema = Joi.object({ - phone: Joi.string().trim().required(), + phone: Joi.string().trim().required().messages({ + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }); export const Enable2FAInputSchema = Joi.object({ - code: Joi.string().trim().required(), + code: Joi.string().trim().required().label('Code').messages({ + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }); export const EnableUser2FASchema = Joi.object({ @@ -79,22 +175,45 @@ export const EnableUser2FASchema = Joi.object({ }).xor('phone', 'email'); export const UserInviteTokenSignupInputSchema = Joi.object({ - email: Joi.string().email({ tlds: { allow: false } }), + email: Joi.string() + .email({ tlds: { allow: false } }) + .messages({ + 'string.email': ExceptionMessage.INVALID_EMAIL_ERROR, + }), phone: Joi.number(), firstName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .required(), + .required() + .label('First name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), middleName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .allow('', null), + .allow('', null) + .label('Middle name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + }), lastName: Joi.string() .regex(/^[a-zA-Z ]*$/) - .required(), + .required() + .label('Last name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }) .options({ abortEarly: false }) .or('email', 'phone'); export const UserPasswordForInviteInputSchema = Joi.object({ - inviteToken: Joi.string().required(), - password: Joi.string().required().min(10), + inviteToken: Joi.string().required().label('Invite Token').messages({ + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), + password: Joi.string().required().min(10).label('Password').messages({ + 'string.min': ExceptionMessage.PASSWORD_MIN_CHAR_LENGTH_ERROR, + 'any.required': ExceptionMessage.REQUIRED_ERROR, + }), }).options({ abortEarly: false }); diff --git a/src/authorization/validation/user.validation.schema.ts b/src/authorization/validation/user.validation.schema.ts index 72f3139..faee9ee 100644 --- a/src/authorization/validation/user.validation.schema.ts +++ b/src/authorization/validation/user.validation.schema.ts @@ -1,8 +1,24 @@ import * as Joi from '@hapi/joi'; +import { ExceptionMessage } from '../../../src/constants/exception.message'; export const UpdateUserSchema = Joi.object({ - firstName: Joi.string().regex(/^[a-zA-Z ]*$/), - middleName: Joi.string().regex(/^[a-zA-Z ]*$/), - lastName: Joi.string().regex(/^[a-zA-Z ]*$/), + firstName: Joi.string() + .regex(/^[a-zA-Z ]*$/) + .label('First name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + }), + middleName: Joi.string() + .regex(/^[a-zA-Z ]*$/) + .label('Middle name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + }), + lastName: Joi.string() + .regex(/^[a-zA-Z ]*$/) + .label('Last name') + .messages({ + 'string.pattern.base': ExceptionMessage.CHAR_ONLY_ERROR, + }), groups: Joi.array(), }).options({ abortEarly: false }); diff --git a/src/constants/exception.message.enum.ts b/src/constants/exception.message.enum.ts new file mode 100644 index 0000000..93350ae --- /dev/null +++ b/src/constants/exception.message.enum.ts @@ -0,0 +1,8 @@ +export enum ExceptionType { + CHAR_ONLY_ERROR = 'CHAR_ONLY_ERROR', + REQUIRED_ERROR = 'REQUIRED_ERROR', + INVALID_EMAIL_ERROR = 'INVALID_EMAIL_ERROR', + INVALID_PHONE_ERROR = 'INVALID_PHONE_ERROR', + PASSWORD_MIN_CHAR_LENGTH_ERROR = 'PASSWORD_MIN_CHAR_LENGTH_ERROR', + NEW_PASSWORD_MATCH_CURRENT_PASSWORD_ERROR = 'NEW_PASSWORD_MATCH_CURRENT_PASSWORD_ERROR', +} diff --git a/src/constants/exception.message.ts b/src/constants/exception.message.ts new file mode 100644 index 0000000..b5cb758 --- /dev/null +++ b/src/constants/exception.message.ts @@ -0,0 +1,12 @@ +import { ExceptionType } from './exception.message.enum'; + +export const ExceptionMessage: { [key in ExceptionType]: string } = { + [ExceptionType.CHAR_ONLY_ERROR]: '{{#label}} should contain only characters', + [ExceptionType.REQUIRED_ERROR]: '{{#label}} is mandatory', + [ExceptionType.INVALID_EMAIL_ERROR]: 'Invalid email address', + [ExceptionType.INVALID_PHONE_ERROR]: 'Invalid phone number', + [ExceptionType.PASSWORD_MIN_CHAR_LENGTH_ERROR]: + '{{#label}} should have a minimum of 10 characters', + [ExceptionType.NEW_PASSWORD_MATCH_CURRENT_PASSWORD_ERROR]: + 'New Password should not match the current password', +}; diff --git a/src/exception/exception.filter.ts b/src/exception/exception.filter.ts index cf414c5..74497b4 100644 --- a/src/exception/exception.filter.ts +++ b/src/exception/exception.filter.ts @@ -4,8 +4,8 @@ import { HttpException, HttpStatus, } from '@nestjs/common'; -import { Response } from 'express'; import { GqlExceptionFilter } from '@nestjs/graphql'; +import { Response } from 'express'; import { LoggerService } from '../logger/logger.service'; @Catch() diff --git a/src/validation/validation.pipe.ts b/src/validation/validation.pipe.ts index 7fd8b34..b2d79b5 100644 --- a/src/validation/validation.pipe.ts +++ b/src/validation/validation.pipe.ts @@ -1,10 +1,10 @@ +import { ObjectSchema } from '@hapi/joi'; import { - PipeTransform, - Injectable, ArgumentMetadata, BadRequestException, + Injectable, + PipeTransform, } from '@nestjs/common'; -import { ObjectSchema } from '@hapi/joi'; @Injectable() export default class ValidationPipe implements PipeTransform { @@ -17,10 +17,7 @@ export default class ValidationPipe implements PipeTransform { const { error } = this.schema.validate(value); if (error) { - throw new BadRequestException( - 'Failed to validate the input payload', - error.message, - ); + throw new BadRequestException(error.message); } return value; }