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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ export class UpdateNotificationsSettingsRequestDto {
isSmsEnabled!: boolean;
@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)
fcmToken?: string;
}

View File

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

View File

@ -4,7 +4,7 @@ import { Roles } from '~/auth/enums';
import { IJwtPayload } from '~/auth/interfaces';
import { AllowedRoles, AuthenticatedUser } from '~/common/decorators';
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 { CreateGiftRequestDto, GiftFiltersRequestDto, GiftReplyRequestDto } from '../dtos/request';
import { GiftDetailsResponseDto, GiftListResponseDto } from '../dtos/response';
@ -13,6 +13,7 @@ import { GiftsService } from '../services';
@Controller('gift')
@ApiTags('Gifts')
@ApiBearerAuth()
@ApiLangRequestHeader()
export class GiftsController {
constructor(private readonly giftsService: GiftsService) {}

View File

@ -4,6 +4,102 @@
"paginationSize": "حجم الصفحة",
"document": {
"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": "يجب تسجيل الدخول مره اخرى",

View File

@ -4,7 +4,103 @@
"paginationSize": "page size",
"document": {
"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",

View File

@ -24,5 +24,6 @@
"IsValidExpiryDate": "$t({path}.PROPERTY_MAPPINGS.{property}) must be a valid expiry date",
"IsEnglishOnly": "$t({path}.PROPERTY_MAPPINGS.{property}) must be in English",
"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 { AllowedRoles, AuthenticatedUser, Public } from '~/common/decorators';
import { RolesGuard } from '~/common/guards';
import { ApiDataPageResponse, ApiDataResponse } from '~/core/decorators';
import { ApiDataPageResponse, ApiDataResponse, ApiLangRequestHeader } from '~/core/decorators';
import { PageOptionsRequestDto } from '~/core/dtos';
import { CustomParseUUIDPipe } from '~/core/pipes';
import { ResponseFactory } from '~/core/utils';
@ -15,6 +15,7 @@ import { JuniorService } from '../services';
@Controller('juniors')
@ApiTags('Juniors')
@ApiBearerAuth()
@ApiLangRequestHeader()
export class JuniorController {
constructor(private readonly juniorService: JuniorService) {}

View File

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

View File

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

View File

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

View File

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

View File

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