feat: add localization for request dto validations and add accept lanuage header

This commit is contained in:
Abdalhamid Alhamad
2025-01-14 14:59:01 +03:00
parent 6d2d2b558a
commit 8ff9f921e8
16 changed files with 223 additions and 17 deletions

View File

@ -4,7 +4,7 @@ import { Roles } from '~/auth/enums';
import { IJwtPayload } from '~/auth/interfaces'; import { IJwtPayload } from '~/auth/interfaces';
import { AllowedRoles, AuthenticatedUser } from '~/common/decorators'; import { AllowedRoles, AuthenticatedUser } from '~/common/decorators';
import { RolesGuard } from '~/common/guards'; import { RolesGuard } from '~/common/guards';
import { ApiDataPageResponse, ApiDataResponse } from '~/core/decorators'; import { ApiDataPageResponse, ApiDataResponse, ApiLangRequestHeader } from '~/core/decorators';
import { PageOptionsRequestDto } from '~/core/dtos'; import { PageOptionsRequestDto } from '~/core/dtos';
import { CustomParseUUIDPipe } from '~/core/pipes'; import { CustomParseUUIDPipe } from '~/core/pipes';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
@ -15,6 +15,7 @@ import { AllowanceChangeRequestsService } from '../services';
@Controller('allowance-change-requests') @Controller('allowance-change-requests')
@ApiTags('Allowance Change Requests') @ApiTags('Allowance Change Requests')
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class AllowanceChangeRequestController { export class AllowanceChangeRequestController {
constructor(private readonly allowanceChangeRequestsService: AllowanceChangeRequestsService) {} constructor(private readonly allowanceChangeRequestsService: AllowanceChangeRequestsService) {}

View File

@ -4,7 +4,7 @@ import { Roles } from '~/auth/enums';
import { IJwtPayload } from '~/auth/interfaces'; import { IJwtPayload } from '~/auth/interfaces';
import { AllowedRoles, AuthenticatedUser } from '~/common/decorators'; import { AllowedRoles, AuthenticatedUser } from '~/common/decorators';
import { RolesGuard } from '~/common/guards'; import { RolesGuard } from '~/common/guards';
import { ApiDataPageResponse, ApiDataResponse } from '~/core/decorators'; import { ApiDataPageResponse, ApiDataResponse, ApiLangRequestHeader } from '~/core/decorators';
import { PageOptionsRequestDto } from '~/core/dtos'; import { PageOptionsRequestDto } from '~/core/dtos';
import { CustomParseUUIDPipe } from '~/core/pipes'; import { CustomParseUUIDPipe } from '~/core/pipes';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
@ -15,6 +15,7 @@ import { AllowancesService } from '../services';
@Controller('allowances') @Controller('allowances')
@ApiTags('Allowances') @ApiTags('Allowances')
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class AllowancesController { export class AllowancesController {
constructor(private readonly allowancesService: AllowancesService) {} constructor(private readonly allowancesService: AllowancesService) {}

View File

@ -4,6 +4,7 @@ import { Request } from 'express';
import { DEVICE_ID_HEADER } from '~/common/constants'; import { DEVICE_ID_HEADER } from '~/common/constants';
import { AuthenticatedUser, Public } from '~/common/decorators'; import { AuthenticatedUser, Public } from '~/common/decorators';
import { AccessTokenGuard } from '~/common/guards'; import { AccessTokenGuard } from '~/common/guards';
import { ApiLangRequestHeader } from '~/core/decorators';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
import { import {
CreateUnverifiedUserRequestDto, CreateUnverifiedUserRequestDto,
@ -27,6 +28,7 @@ import { AuthService } from '../services';
@Controller('auth') @Controller('auth')
@ApiTags('Auth') @ApiTags('Auth')
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class AuthController { export class AuthController {
constructor(private readonly authService: AuthService) {} constructor(private readonly authService: AuthService) {}
@Post('register/otp') @Post('register/otp')

View File

@ -4,7 +4,7 @@ import { IJwtPayload } from '~/auth/interfaces';
import { DEVICE_ID_HEADER } from '~/common/constants'; import { DEVICE_ID_HEADER } from '~/common/constants';
import { AuthenticatedUser } from '~/common/decorators'; import { AuthenticatedUser } from '~/common/decorators';
import { AccessTokenGuard } from '~/common/guards'; import { AccessTokenGuard } from '~/common/guards';
import { ApiDataResponse } from '~/core/decorators'; import { ApiDataResponse, ApiLangRequestHeader } from '~/core/decorators';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
import { UpdateCustomerRequestDto, UpdateNotificationsSettingsRequestDto } from '../dtos/request'; import { UpdateCustomerRequestDto, UpdateNotificationsSettingsRequestDto } from '../dtos/request';
import { CustomerResponseDto, NotificationSettingsResponseDto } from '../dtos/response'; import { CustomerResponseDto, NotificationSettingsResponseDto } from '../dtos/response';
@ -13,6 +13,7 @@ import { CustomerService } from '../services';
@Controller('customers') @Controller('customers')
@ApiTags('Customers') @ApiTags('Customers')
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class CustomerController { export class CustomerController {
constructor(private readonly customerService: CustomerService) {} constructor(private readonly customerService: CustomerService) {}

View File

@ -18,7 +18,7 @@ export class UpdateNotificationsSettingsRequestDto {
isSmsEnabled!: boolean; isSmsEnabled!: boolean;
@ApiPropertyOptional() @ApiPropertyOptional()
@IsString({ message: i18n('validation.isString', { path: 'general', property: 'customer.fcmToken' }) }) @IsString({ message: i18n('validation.isString', { path: 'general', property: 'auth.fcmToken' }) })
@ValidateIf((o) => o.isPushEnabled) @ValidateIf((o) => o.isPushEnabled)
fcmToken?: string; fcmToken?: string;
} }

View File

@ -5,6 +5,7 @@ import { memoryStorage } from 'multer';
import { IJwtPayload } from '~/auth/interfaces'; import { IJwtPayload } from '~/auth/interfaces';
import { AuthenticatedUser } from '~/common/decorators'; import { AuthenticatedUser } from '~/common/decorators';
import { AccessTokenGuard } from '~/common/guards'; import { AccessTokenGuard } from '~/common/guards';
import { ApiLangRequestHeader } from '~/core/decorators';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
import { UploadDocumentRequestDto } from '../dtos/request'; import { UploadDocumentRequestDto } from '../dtos/request';
import { DocumentMetaResponseDto } from '../dtos/response'; import { DocumentMetaResponseDto } from '../dtos/response';
@ -14,6 +15,7 @@ import { DocumentService } from '../services';
@ApiTags('Document') @ApiTags('Document')
@ApiBearerAuth() @ApiBearerAuth()
@UseGuards(AccessTokenGuard) @UseGuards(AccessTokenGuard)
@ApiLangRequestHeader()
export class DocumentController { export class DocumentController {
constructor(private readonly documentService: DocumentService) {} constructor(private readonly documentService: DocumentService) {}

View File

@ -4,7 +4,7 @@ import { Roles } from '~/auth/enums';
import { IJwtPayload } from '~/auth/interfaces'; import { IJwtPayload } from '~/auth/interfaces';
import { AllowedRoles, AuthenticatedUser } from '~/common/decorators'; import { AllowedRoles, AuthenticatedUser } from '~/common/decorators';
import { AccessTokenGuard, RolesGuard } from '~/common/guards'; import { AccessTokenGuard, RolesGuard } from '~/common/guards';
import { ApiDataPageResponse, ApiDataResponse } from '~/core/decorators'; import { ApiDataPageResponse, ApiDataResponse, ApiLangRequestHeader } from '~/core/decorators';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
import { CreateGiftRequestDto, GiftFiltersRequestDto, GiftReplyRequestDto } from '../dtos/request'; import { CreateGiftRequestDto, GiftFiltersRequestDto, GiftReplyRequestDto } from '../dtos/request';
import { GiftDetailsResponseDto, GiftListResponseDto } from '../dtos/response'; import { GiftDetailsResponseDto, GiftListResponseDto } from '../dtos/response';
@ -13,6 +13,7 @@ import { GiftsService } from '../services';
@Controller('gift') @Controller('gift')
@ApiTags('Gifts') @ApiTags('Gifts')
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class GiftsController { export class GiftsController {
constructor(private readonly giftsService: GiftsService) {} constructor(private readonly giftsService: GiftsService) {}

View File

@ -4,6 +4,102 @@
"paginationSize": "حجم الصفحة", "paginationSize": "حجم الصفحة",
"document": { "document": {
"documentType": "نوع الملف" "documentType": "نوع الملف"
},
"auth": {
"countryCode": "رمز الدولة",
"phoneNumber": "رقم الهاتف",
"deviceId": "معرّف الجهاز",
"publicKey": "المفتاح العام",
"email": "البريد الإلكتروني",
"password": "كلمة المرور",
"confirmPassword": "تأكيد كلمة المرور",
"otp": "رمز التحقق",
"grantType": "نوع الإذن",
"signature": "التوقيع",
"googleToken": "رمز جوجل",
"fcmToken": "رمز FCM",
"refreshToken": "رمز التحديث",
"qrToken": "رمز QR",
"passcode": "رمز المرور"
},
"customer": {
"firstName": "الاسم الأول",
"lastName": "اسم العائلة",
"countryOfResidence": "بلد الإقامة",
"dateOfBirth": "تاريخ الميلاد",
"profilePictureId": "معرّف صورة الملف الشخصي",
"isEmailEnabled": "هل البريد الإلكتروني مفعّل",
"isSmsEnabled": "هل الرسائل النصية مفعّلة",
"isPushEnabled": "هل الإشعارات مفعّلة"
},
"junior": {
"relationship": "العلاقة",
"civilIdFrontId": "الوجه الأمامي لبطاقة الهوية المدنية",
"civilIdBackId": "الوجه الخلفي لبطاقة الهوية المدنية",
"color": "اللون",
"avatarId": "معرّف الصورة الرمزية"
},
"moneyRequest": {
"requestedAmount": "المبلغ المطلوب",
"message": "الرسالة",
"frequency": "التكرار",
"startDate": "تاريخ البدء",
"endDate": "تاريخ النهاية",
"status": "الحالة"
},
"goal": {
"category": {
"name": "الاسم"
},
"name": "الاسم",
"description": "الوصف",
"dueDate": "تاريخ النهاية",
"targetAmount": "المبلغ المستهدف",
"categoryIds": "معرّفات التصنيفات",
"imageId": "معرّف الصورة",
"fundAmount": "المبلغ الممول"
},
"task": {
"title": "العنوان",
"description": "الوصف",
"startDate": "تاريخ البدء",
"dueDate": "تاريخ النهاية",
"rewardAmount": "قيمة المكافأة",
"frequency": "التكرار",
"isProofRequired": "هل يتطلب إثبات",
"imageId": "معرّف الصورة",
"juniorId": "معرّف الطفل",
"status": "الحالة"
},
"gift": {
"name": "الاسم",
"description": "الوصف",
"color": "اللون",
"amount": "المبلغ",
"imageId": "معرّف الصورة",
"recipientId": "معرّف المستلم",
"status": "الحالة"
},
"allowance": {
"name": "الاسم",
"amount": "المبلغ",
"type": "النوع",
"startDate": "تاريخ البدء",
"endDate": "تاريخ النهاية",
"numberOfTransactions": "عدد المعاملات",
"juniorId": "معرّف الطفل"
},
"allowanceChangeRequest": {
"reason": "السبب",
"allowanceId": "معرّف المصروف",
"amount": "المبلغ"
} }
}, },
"UNAUTHORIZED_ERROR": "يجب تسجيل الدخول مره اخرى", "UNAUTHORIZED_ERROR": "يجب تسجيل الدخول مره اخرى",

View File

@ -4,7 +4,103 @@
"paginationSize": "page size", "paginationSize": "page size",
"document": { "document": {
"documentType": "Document type" "documentType": "Document type"
},
"auth": {
"countryCode": "Country code",
"phoneNumber": "Phone number",
"deviceId": "Device ID",
"publicKey": "Public key",
"email": "Email",
"password": "Password",
"confirmPassword": "Confirm password",
"otp": "OTP",
"grantType": "Grant type",
"signature": "Signature",
"googleToken": "Google token",
"fcmToken": "FCM token",
"refreshToken": "Refresh token",
"qrToken": "QR token",
"passcode": "Passcode"
},
"customer": {
"firstName": "First name",
"lastName": "Last name",
"countryOfResidence": "Country of residence",
"dateOfBirth": "Date of birth",
"profilePictureId": "Profile picture ID",
"isEmailEnabled": "Is Email enabled",
"isSmsEnabled": "Is SMS enabled",
"isPushEnabled": "Is Push enabled"
},
"junior": {
"relationship": "Relationship",
"civilIdFrontId": "Civil ID front",
"civilIdBackId": "Civil ID back",
"color": "Color",
"avatarId": "Avatar ID"
},
"moneyRequest": {
"requestedAmount": "Requested amount",
"message": "Message",
"frequency": "Frequency",
"startDate": "Start date",
"endDate": "End date",
"status": "Status"
},
"goal": {
"category": {
"name": "Name"
},
"name": "Name",
"description": "Description",
"dueDate": "Due date",
"targetAmount": "Target amount",
"categoryIds": "Category IDs",
"imageId": "Image ID",
"fundAmount": "Fund amount"
},
"task": {
"title": "Title",
"description": "Description",
"startDate": "Start date",
"dueDate": "Due date",
"rewardAmount": "Reward amount",
"frequency": "Frequency",
"isProofRequired": "Is proof required",
"imageId": "Image ID",
"juniorId": "Junior ID",
"status": "Status"
},
"gift": {
"name": "Name",
"description": "Description",
"color": "Color",
"amount": "Amount",
"imageId": "Image ID",
"recipientId": "Recipient ID",
"status": "Status"
},
"allowance": {
"name": "Name",
"amount": "Amount",
"type": "Type",
"startDate": "Start date",
"endDate": "End date",
"numberOfTransactions": "Number of transactions",
"juniorId": "Junior ID"
},
"allowanceChangeRequest": {
"reason": "Reason",
"allowanceId": "Allowance ID",
"amount": "Amount"
} }
}, },
"UNAUTHORIZED_ERROR": "You have to login again", "UNAUTHORIZED_ERROR": "You have to login again",

View File

@ -24,5 +24,6 @@
"IsValidExpiryDate": "$t({path}.PROPERTY_MAPPINGS.{property}) must be a valid expiry date", "IsValidExpiryDate": "$t({path}.PROPERTY_MAPPINGS.{property}) must be a valid expiry date",
"IsEnglishOnly": "$t({path}.PROPERTY_MAPPINGS.{property}) must be in English", "IsEnglishOnly": "$t({path}.PROPERTY_MAPPINGS.{property}) must be in English",
"IsAbove18": "$t({path}.PROPERTY_MAPPINGS.{property}) must be above 18 years", "IsAbove18": "$t({path}.PROPERTY_MAPPINGS.{property}) must be above 18 years",
"IsValidPhoneNumber": "$t({path}.PROPERTY_MAPPINGS.{property}) must be a valid phone number" "IsValidPhoneNumber": "$t({path}.PROPERTY_MAPPINGS.{property}) must be a valid phone number",
"IsPositive": "$t({path}.PROPERTY_MAPPINGS.{property}) must be a positive number"
} }

View File

@ -4,7 +4,7 @@ import { Roles } from '~/auth/enums';
import { IJwtPayload } from '~/auth/interfaces'; import { IJwtPayload } from '~/auth/interfaces';
import { AllowedRoles, AuthenticatedUser, Public } from '~/common/decorators'; import { AllowedRoles, AuthenticatedUser, Public } from '~/common/decorators';
import { RolesGuard } from '~/common/guards'; import { RolesGuard } from '~/common/guards';
import { ApiDataPageResponse, ApiDataResponse } from '~/core/decorators'; import { ApiDataPageResponse, ApiDataResponse, ApiLangRequestHeader } from '~/core/decorators';
import { PageOptionsRequestDto } from '~/core/dtos'; import { PageOptionsRequestDto } from '~/core/dtos';
import { CustomParseUUIDPipe } from '~/core/pipes'; import { CustomParseUUIDPipe } from '~/core/pipes';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
@ -15,6 +15,7 @@ import { JuniorService } from '../services';
@Controller('juniors') @Controller('juniors')
@ApiTags('Juniors') @ApiTags('Juniors')
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class JuniorController { export class JuniorController {
constructor(private readonly juniorService: JuniorService) {} constructor(private readonly juniorService: JuniorService) {}

View File

@ -18,13 +18,13 @@ export class CreateJuniorUserRequestDto {
phoneNumber!: string; phoneNumber!: string;
@ApiProperty({ example: 'John' }) @ApiProperty({ example: 'John' })
@IsString({ message: i18n('validation.IsString', { path: 'general', property: 'auth.firstName' }) }) @IsString({ message: i18n('validation.IsString', { path: 'general', property: 'customer.firstName' }) })
@IsNotEmpty({ message: i18n('validation.IsNotEmpty', { path: 'general', property: 'auth.firstName' }) }) @IsNotEmpty({ message: i18n('validation.IsNotEmpty', { path: 'general', property: 'customer.firstName' }) })
firstName!: string; firstName!: string;
@ApiProperty({ example: 'Doe' }) @ApiProperty({ example: 'Doe' })
@IsString({ message: i18n('validation.IsString', { path: 'general', property: 'auth.lastName' }) }) @IsString({ message: i18n('validation.IsString', { path: 'general', property: 'customer.lastName' }) })
@IsNotEmpty({ message: i18n('validation.IsNotEmpty', { path: 'general', property: 'auth.lastName' }) }) @IsNotEmpty({ message: i18n('validation.IsNotEmpty', { path: 'general', property: 'customer.lastName' }) })
lastName!: string; lastName!: string;
@ApiProperty({ example: '2020-01-01' }) @ApiProperty({ example: '2020-01-01' })

View File

@ -1,13 +1,14 @@
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsUUID } from 'class-validator'; import { IsEnum, IsUUID } from 'class-validator';
import { i18nValidationMessage as i18n } from 'nestjs-i18n';
import { ThemeColor } from '~/junior/enums'; import { ThemeColor } from '~/junior/enums';
export class SetThemeRequestDto { export class SetThemeRequestDto {
@ApiProperty({ example: ThemeColor.VIOLET }) @ApiProperty({ example: ThemeColor.VIOLET })
@IsEnum(ThemeColor) @IsEnum(ThemeColor, { message: i18n('validation.IsEnum', { path: 'general', property: 'junior.color' }) })
color!: ThemeColor; color!: ThemeColor;
@ApiProperty({ example: 'fbfre-4f4f-4f4f-4f4f' }) @ApiProperty({ example: 'fbfre-4f4f-4f4f-4f4f' })
@IsUUID() @IsUUID('4', { message: i18n('validation.IsUUID', { path: 'general', property: 'junior.avatarId' }) })
avatarId!: string; avatarId!: string;
} }

View File

@ -4,7 +4,7 @@ import { Roles } from '~/auth/enums';
import { IJwtPayload } from '~/auth/interfaces'; import { IJwtPayload } from '~/auth/interfaces';
import { AllowedRoles, AuthenticatedUser } from '~/common/decorators'; import { AllowedRoles, AuthenticatedUser } from '~/common/decorators';
import { RolesGuard } from '~/common/guards'; import { RolesGuard } from '~/common/guards';
import { ApiDataPageResponse, ApiDataResponse } from '~/core/decorators'; import { ApiDataPageResponse, ApiDataResponse, ApiLangRequestHeader } from '~/core/decorators';
import { CustomParseUUIDPipe } from '~/core/pipes'; import { CustomParseUUIDPipe } from '~/core/pipes';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
import { CreateMoneyRequestRequestDto, MoneyRequestsFiltersRequestDto } from '../dtos/request'; import { CreateMoneyRequestRequestDto, MoneyRequestsFiltersRequestDto } from '../dtos/request';
@ -14,6 +14,7 @@ import { MoneyRequestsService } from '../services';
@Controller('money-requests') @Controller('money-requests')
@ApiTags('Money Requests') @ApiTags('Money Requests')
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class MoneyRequestsController { export class MoneyRequestsController {
constructor(private readonly moneyRequestsService: MoneyRequestsService) {} constructor(private readonly moneyRequestsService: MoneyRequestsService) {}

View File

@ -4,7 +4,7 @@ import { Roles } from '~/auth/enums';
import { IJwtPayload } from '~/auth/interfaces'; import { IJwtPayload } from '~/auth/interfaces';
import { AllowedRoles, AuthenticatedUser } from '~/common/decorators'; import { AllowedRoles, AuthenticatedUser } from '~/common/decorators';
import { RolesGuard } from '~/common/guards'; import { RolesGuard } from '~/common/guards';
import { ApiDataPageResponse, ApiDataResponse } from '~/core/decorators'; import { ApiDataPageResponse, ApiDataResponse, ApiLangRequestHeader } from '~/core/decorators';
import { PageOptionsRequestDto } from '~/core/dtos'; import { PageOptionsRequestDto } from '~/core/dtos';
import { CustomParseUUIDPipe } from '~/core/pipes'; import { CustomParseUUIDPipe } from '~/core/pipes';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
@ -23,6 +23,7 @@ import { CategoryService } from '../services/category.service';
@UseGuards(RolesGuard) @UseGuards(RolesGuard)
@AllowedRoles(Roles.JUNIOR) @AllowedRoles(Roles.JUNIOR)
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class SavingGoalsController { export class SavingGoalsController {
constructor( constructor(
private readonly savingGoalsService: SavingGoalsService, private readonly savingGoalsService: SavingGoalsService,

View File

@ -4,7 +4,7 @@ import { Roles } from '~/auth/enums';
import { IJwtPayload } from '~/auth/interfaces'; import { IJwtPayload } from '~/auth/interfaces';
import { AllowedRoles, AuthenticatedUser } from '~/common/decorators'; import { AllowedRoles, AuthenticatedUser } from '~/common/decorators';
import { AccessTokenGuard, RolesGuard } from '~/common/guards'; import { AccessTokenGuard, RolesGuard } from '~/common/guards';
import { ApiDataPageResponse } from '~/core/decorators'; import { ApiDataPageResponse, ApiLangRequestHeader } from '~/core/decorators';
import { CustomParseUUIDPipe } from '~/core/pipes'; import { CustomParseUUIDPipe } from '~/core/pipes';
import { ResponseFactory } from '~/core/utils'; import { ResponseFactory } from '~/core/utils';
import { CreateTaskRequestDto, TaskSubmissionRequestDto } from '../dtos/request'; import { CreateTaskRequestDto, TaskSubmissionRequestDto } from '../dtos/request';
@ -15,6 +15,7 @@ import { TaskService } from '../services';
@Controller('tasks') @Controller('tasks')
@ApiTags('Tasks') @ApiTags('Tasks')
@ApiBearerAuth() @ApiBearerAuth()
@ApiLangRequestHeader()
export class TaskController { export class TaskController {
constructor(private readonly taskService: TaskService) {} constructor(private readonly taskService: TaskService) {}