From 7986151af479edf2a9b2bd779e5fee1eb1cf9033 Mon Sep 17 00:00:00 2001 From: Dmitri Date: Sun, 16 Nov 2025 18:24:18 +0100 Subject: [PATCH] esercizio --- package-lock.json | 56 ++++++++++++++++--- src/info/info.controller.ts | 9 ++- src/info/info.module.ts | 5 +- src/info/info.service.ts | 21 ++++++- src/info/interfaces/index.ts | 7 +++ src/info/models/index.ts | 28 +++++++++- src/info/validators/age-match-date-birth.ts | 61 +++++++++++++++++++++ 7 files changed, 170 insertions(+), 17 deletions(-) create mode 100644 src/info/validators/age-match-date-birth.ts diff --git a/package-lock.json b/package-lock.json index 8c60992..2fa868b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -225,6 +225,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", "dev": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -1524,6 +1525,7 @@ "version": "9.2.0", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.2.0.tgz", "integrity": "sha512-Ndcqak/ETYi+n1c5lFRPbxKLyUuM6DIOxcvfEFGfi0f6ad4dWDXRDx7z/n8V0l8+Y8djvvOHgf3t0e93w963Qg==", + "peer": true, "dependencies": { "iterare": "1.2.1", "tslib": "2.4.1", @@ -1557,6 +1559,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.2.0.tgz", "integrity": "sha512-eVN7aXAavV+ImVt8mO+rQ5YyUP6lJtQKUtQHxHKzz6Wg+9Y67WWZS2uDcDX5NNcNijbWky5bqad86fgcK9Oqig==", "hasInstallScript": true, + "peer": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -1594,6 +1597,7 @@ "version": "9.2.0", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.2.0.tgz", "integrity": "sha512-J1+nnzjC9ATSb0jSHBqAE6D4o+PIbGPItEfYTOZ0rkE5bvqnRfgO4q94SXhfri+5PaNx2vM8tOZsKaD0QmQRGQ==", + "peer": true, "dependencies": { "body-parser": "1.20.1", "cors": "2.8.5", @@ -2022,7 +2026,8 @@ "version": "16.18.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", "integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -2142,6 +2147,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz", "integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.44.0", "@typescript-eslint/types": "5.44.0", @@ -2466,6 +2472,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2886,6 +2893,7 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -3053,6 +3061,7 @@ "url": "https://paulmillr.com/funding/" } ], + "peer": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3096,12 +3105,14 @@ "node_modules/class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "peer": true }, "node_modules/class-validator": { "version": "0.13.2", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", + "peer": true, "dependencies": { "libphonenumber-js": "^1.9.43", "validator": "^13.7.0" @@ -3600,6 +3611,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", "dev": true, + "peer": true, "dependencies": { "@eslint/eslintrc": "^1.3.3", "@humanwhocodes/config-array": "^0.11.6", @@ -4966,6 +4978,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", "dev": true, + "peer": true, "dependencies": { "@jest/core": "^28.1.3", "@jest/types": "^28.1.3", @@ -6666,6 +6679,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==", "dev": true, + "peer": true, "bin": { "prettier": "bin-prettier.js" }, @@ -6881,7 +6895,8 @@ "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "peer": true }, "node_modules/regexpp": { "version": "3.2.0", @@ -7042,6 +7057,7 @@ "version": "7.5.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -7093,6 +7109,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -7807,6 +7824,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -7979,6 +7997,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8549,6 +8568,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.2.tgz", "integrity": "sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==", "dev": true, + "peer": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.18.6", @@ -9555,6 +9575,7 @@ "version": "9.2.0", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.2.0.tgz", "integrity": "sha512-Ndcqak/ETYi+n1c5lFRPbxKLyUuM6DIOxcvfEFGfi0f6ad4dWDXRDx7z/n8V0l8+Y8djvvOHgf3t0e93w963Qg==", + "peer": true, "requires": { "iterare": "1.2.1", "tslib": "2.4.1", @@ -9565,6 +9586,7 @@ "version": "9.2.0", "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.2.0.tgz", "integrity": "sha512-eVN7aXAavV+ImVt8mO+rQ5YyUP6lJtQKUtQHxHKzz6Wg+9Y67WWZS2uDcDX5NNcNijbWky5bqad86fgcK9Oqig==", + "peer": true, "requires": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", @@ -9579,6 +9601,7 @@ "version": "9.2.0", "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.2.0.tgz", "integrity": "sha512-J1+nnzjC9ATSb0jSHBqAE6D4o+PIbGPItEfYTOZ0rkE5bvqnRfgO4q94SXhfri+5PaNx2vM8tOZsKaD0QmQRGQ==", + "peer": true, "requires": { "body-parser": "1.20.1", "cors": "2.8.5", @@ -9943,7 +9966,8 @@ "version": "16.18.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.3.tgz", "integrity": "sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg==", - "dev": true + "dev": true, + "peer": true }, "@types/parse-json": { "version": "4.0.0", @@ -10047,6 +10071,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz", "integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "5.44.0", "@typescript-eslint/types": "5.44.0", @@ -10294,7 +10319,8 @@ "version": "8.8.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", - "dev": true + "dev": true, + "peer": true }, "acorn-import-assertions": { "version": "1.8.0", @@ -10604,6 +10630,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, + "peer": true, "requires": { "caniuse-lite": "^1.0.30001400", "electron-to-chromium": "^1.4.251", @@ -10711,6 +10738,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, + "peer": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -10743,12 +10771,14 @@ "class-transformer": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "peer": true }, "class-validator": { "version": "0.13.2", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", + "peer": true, "requires": { "libphonenumber-js": "^1.9.43", "validator": "^13.7.0" @@ -11130,6 +11160,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", "dev": true, + "peer": true, "requires": { "@eslint/eslintrc": "^1.3.3", "@humanwhocodes/config-array": "^0.11.6", @@ -12145,6 +12176,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", "dev": true, + "peer": true, "requires": { "@jest/core": "^28.1.3", "@jest/types": "^28.1.3", @@ -13414,7 +13446,8 @@ "version": "2.8.0", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz", "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==", - "dev": true + "dev": true, + "peer": true }, "prettier-linter-helpers": { "version": "1.0.0", @@ -13572,7 +13605,8 @@ "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "peer": true }, "regexpp": { "version": "3.2.0", @@ -13675,6 +13709,7 @@ "version": "7.5.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "peer": true, "requires": { "tslib": "^2.1.0" } @@ -13705,6 +13740,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -14234,6 +14270,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", "dev": true, + "peer": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -14353,7 +14390,8 @@ "version": "4.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", - "dev": true + "dev": true, + "peer": true }, "universalify": { "version": "2.0.0", diff --git a/src/info/info.controller.ts b/src/info/info.controller.ts index f5be68c..56a52f1 100644 --- a/src/info/info.controller.ts +++ b/src/info/info.controller.ts @@ -1,14 +1,19 @@ import { Controller, Post, Body } from '@nestjs/common'; import { InfoService } from './info.service'; -import { UpdateInfoRequest } from './interfaces'; +import { UpdateFormRequest, UpdateInfoRequest } from './interfaces'; import { BaseResponse } from '../interfaces'; @Controller('info') export class InfoController { - constructor(private readonly infoService: InfoService) {} + constructor(private readonly infoService: InfoService) { } @Post('/validate') getConfig(@Body() bodyRequest: UpdateInfoRequest): Promise { return this.infoService.validateInfo(bodyRequest); } + + @Post('/validate-form') + getForm(@Body() bodyRequest: UpdateFormRequest): Promise { + return this.infoService.validateForm(bodyRequest); + } } diff --git a/src/info/info.module.ts b/src/info/info.module.ts index 7cedf96..88090e2 100644 --- a/src/info/info.module.ts +++ b/src/info/info.module.ts @@ -1,10 +1,11 @@ import { Module } from '@nestjs/common'; import { InfoController } from './info.controller'; import { InfoService } from './info.service'; +import { IsAgeMatchingBirthDateConstraint } from './validators/age-match-date-birth'; @Module({ imports: [], controllers: [InfoController], - providers: [InfoService], + providers: [InfoService, IsAgeMatchingBirthDateConstraint], }) -export class InfoModule {} +export class InfoModule { } diff --git a/src/info/info.service.ts b/src/info/info.service.ts index 7453a5a..05599c8 100644 --- a/src/info/info.service.ts +++ b/src/info/info.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { plainToClass } from 'class-transformer'; import { validate } from 'class-validator'; -import { UpdateInfoRequest as UpdateInfoRequestInterface } from './interfaces'; +import { UpdateFormRequest, UpdateInfoRequest as UpdateInfoRequestInterface } from './interfaces'; import { BaseResponse } from '../interfaces'; -import { UpdateInfoRequest } from './models'; +import { UpdateFormValidator, UpdateInfoRequest } from './models'; @Injectable() export class InfoService { @@ -23,4 +23,21 @@ export class InfoService { data, }; } + + async validateForm( + rawData: UpdateFormRequest, + ): Promise { + const data = plainToClass(UpdateFormValidator, rawData); + const validationErrors = await validate(data); + if (validationErrors.length > 0) { + return { + success: false, + errors: validationErrors, + }; + } + return { + success: true, + data, + }; + } } diff --git a/src/info/interfaces/index.ts b/src/info/interfaces/index.ts index 0045845..d49d826 100644 --- a/src/info/interfaces/index.ts +++ b/src/info/interfaces/index.ts @@ -1,3 +1,10 @@ export interface UpdateInfoRequest { name: string; } + +export interface UpdateFormRequest { + name: string + age: number + married?: boolean + dateOfBirth: Date +} diff --git a/src/info/models/index.ts b/src/info/models/index.ts index 5b36a12..290984d 100644 --- a/src/info/models/index.ts +++ b/src/info/models/index.ts @@ -1,5 +1,7 @@ -import { UpdateInfoRequest as UpdateInfoRequestInterface } from '../interfaces'; -import { IsNotEmpty, IsString, MinLength } from 'class-validator'; +import { UpdateFormRequest, UpdateInfoRequest as UpdateInfoRequestInterface } from '../interfaces'; +import { IsDate, IsNotEmpty, IsNumber, IsString, Max, MaxLength, Min, MinLength } from 'class-validator'; +import { Type } from 'class-transformer'; +import { IsAgeMatchingBirthDate } from '../validators/age-match-date-birth'; export class UpdateInfoRequest implements UpdateInfoRequestInterface { @IsNotEmpty() @@ -7,3 +9,25 @@ export class UpdateInfoRequest implements UpdateInfoRequestInterface { @MinLength(3) name: string; } + +export class UpdateFormValidator implements UpdateFormRequest { + @IsNotEmpty() + @IsString() + @MinLength(5) + @MaxLength(50) + name: string; + + @IsNotEmpty() + @IsNumber() + @Max(150) + @Min(1) + @IsAgeMatchingBirthDate({ message: "mismatch betwean date of birth and age" }) + age: number + + married?: boolean + + @IsNotEmpty() + @Type(() => Date) + @IsDate() + dateOfBirth: Date +} diff --git a/src/info/validators/age-match-date-birth.ts b/src/info/validators/age-match-date-birth.ts new file mode 100644 index 0000000..15b66f6 --- /dev/null +++ b/src/info/validators/age-match-date-birth.ts @@ -0,0 +1,61 @@ +import { + ValidatorConstraint, + ValidatorConstraintInterface, + ValidationArguments, + registerDecorator, + ValidationOptions, +} from 'class-validator'; +import { Injectable } from '@nestjs/common'; + +// Funzione helper per calcolare l'età +function calculateAge(birthDate: Date): number { + if (!birthDate || !(birthDate instanceof Date)) return -1; + const today = new Date(); + let age = today.getFullYear() - birthDate.getFullYear(); + const m = today.getMonth() - birthDate.getMonth(); + if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) { + age--; + } + return age; +} + +@ValidatorConstraint({ name: 'isAgeMatchingBirthDate', async: false }) +@Injectable() +export class IsAgeMatchingBirthDateConstraint implements ValidatorConstraintInterface { + + // Questo è il metodo che esegue la validazione + validate(age: number, args: ValidationArguments) { + // args.object è l'intero oggetto DTO che viene validato + const object = args.object as any; + const dateOfBirth = object.dateOfBirth; + + // Se dateOfBirth non è una data valida, questo validatore non fallisce. + // Ci penseranno @IsDate e @IsNotEmpty a bloccare la richiesta prima. + if (!(dateOfBirth instanceof Date)) { + return true; + } + + const calculatedAge = calculateAge(dateOfBirth); + return age === calculatedAge; + } + + // Questo metodo fornisce il messaggio di errore di default + defaultMessage(args: ValidationArguments) { + const calculatedAge = calculateAge((args.object as any).dateOfBirth); + return `The provided age ($value) is incorrect. Based on the date of birth, it should be ${calculatedAge}.`; + } +} + + + +export function IsAgeMatchingBirthDate(validationOptions?: ValidationOptions) { + return function(object: Object, propertyName: string) { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [], + validator: IsAgeMatchingBirthDateConstraint, + }); + }; +}