@hemia/common 0.0.16 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/hemia-common.esm.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { decorate, injectable } from 'inversify';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
5
|
|
|
4
6
|
const METADATA_KEYS = {
|
|
5
7
|
BASE_PATH: 'base_path',
|
|
@@ -378,22 +380,90 @@ function Custom(key) {
|
|
|
378
380
|
}
|
|
379
381
|
|
|
380
382
|
/**
|
|
381
|
-
* Decorador para validar datos de entrada
|
|
383
|
+
* Decorador para validar datos de entrada usando class-validator.
|
|
382
384
|
*
|
|
383
385
|
* @example
|
|
384
|
-
* // Valida el
|
|
385
|
-
* @Validate(
|
|
386
|
-
*
|
|
386
|
+
* // Valida el body usando un DTO
|
|
387
|
+
* async create(@Validate(CreateUserDto) @Body() body: CreateUserDto) {}
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* // Valida con función personalizada
|
|
391
|
+
* @Validate((value) => value.age > 18)
|
|
392
|
+
* async create(@Body('age') age: number) {}
|
|
387
393
|
*
|
|
388
|
-
* @param
|
|
394
|
+
* @param validatorOrDto Clase DTO o función de validación.
|
|
389
395
|
*/
|
|
390
|
-
function Validate(
|
|
396
|
+
function Validate(validatorOrDto) {
|
|
391
397
|
return (target, propertyKey, parameterIndex) => {
|
|
392
398
|
const existingValidators = Reflect.getMetadata(METADATA_KEYS.VALIDATORS, target, propertyKey || '') || [];
|
|
393
|
-
|
|
399
|
+
// Determinar si es una clase DTO o función
|
|
400
|
+
const isClassValidator = typeof validatorOrDto === 'function' && validatorOrDto.prototype;
|
|
401
|
+
const validator = isClassValidator
|
|
402
|
+
? async (value) => {
|
|
403
|
+
const instance = plainToInstance(validatorOrDto, value);
|
|
404
|
+
const errors = await validate(instance, { whitelist: true, forbidNonWhitelisted: true });
|
|
405
|
+
if (errors.length > 0) {
|
|
406
|
+
throw new ValidationException(errors);
|
|
407
|
+
}
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
: validatorOrDto;
|
|
411
|
+
existingValidators.push({ index: parameterIndex, validator, dtoClass: isClassValidator ? validatorOrDto : null });
|
|
394
412
|
Reflect.defineMetadata(METADATA_KEYS.VALIDATORS, existingValidators, target, propertyKey || '');
|
|
395
413
|
};
|
|
396
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* Decorador de método para validar todo el body con un DTO
|
|
417
|
+
* @example
|
|
418
|
+
* @Post()
|
|
419
|
+
* @ValidateDto(CreateUserDto)
|
|
420
|
+
* async create(@Body() body: CreateUserDto) {}
|
|
421
|
+
*/
|
|
422
|
+
function ValidateDto(dtoClass) {
|
|
423
|
+
return (target, propertyKey, descriptor) => {
|
|
424
|
+
const existingValidators = Reflect.getMetadata(METADATA_KEYS.VALIDATORS, target, propertyKey) || [];
|
|
425
|
+
const validator = async (value) => {
|
|
426
|
+
const instance = plainToInstance(dtoClass, value);
|
|
427
|
+
const errors = await validate(instance, {
|
|
428
|
+
whitelist: true,
|
|
429
|
+
forbidNonWhitelisted: true
|
|
430
|
+
});
|
|
431
|
+
if (errors.length > 0) {
|
|
432
|
+
throw new ValidationException(errors);
|
|
433
|
+
}
|
|
434
|
+
return true;
|
|
435
|
+
};
|
|
436
|
+
const paramsMetadata = Reflect.getMetadata(METADATA_KEYS.PARAMS, target, propertyKey) || [];
|
|
437
|
+
const bodyParam = paramsMetadata.find((p) => p.type === ParamType.BODY);
|
|
438
|
+
if (bodyParam) {
|
|
439
|
+
existingValidators.push({
|
|
440
|
+
index: bodyParam.index,
|
|
441
|
+
validator,
|
|
442
|
+
dtoClass
|
|
443
|
+
});
|
|
444
|
+
Reflect.defineMetadata(METADATA_KEYS.VALIDATORS, existingValidators, target, propertyKey);
|
|
445
|
+
}
|
|
446
|
+
return descriptor;
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Excepción personalizada para errores de validación
|
|
451
|
+
*/
|
|
452
|
+
class ValidationException extends Error {
|
|
453
|
+
constructor(errors) {
|
|
454
|
+
super('Validation failed');
|
|
455
|
+
this.statusCode = 400;
|
|
456
|
+
this.errors = errors;
|
|
457
|
+
this.name = 'ValidationException';
|
|
458
|
+
}
|
|
459
|
+
toJSON() {
|
|
460
|
+
return this.errors.map(error => ({
|
|
461
|
+
property: error.property,
|
|
462
|
+
value: error.value,
|
|
463
|
+
constraints: error.constraints
|
|
464
|
+
}));
|
|
465
|
+
}
|
|
466
|
+
}
|
|
397
467
|
|
|
398
468
|
/**
|
|
399
469
|
* Decorador para transformar datos de entrada en un controlador.
|
|
@@ -1102,4 +1172,4 @@ class ApiResponse {
|
|
|
1102
1172
|
}
|
|
1103
1173
|
}
|
|
1104
1174
|
|
|
1105
|
-
export { AllowAny, ApiKey, ApiResponse, BackupError, BadGatewayError, BadRequestError, Body, BusinessRuleViolationError, ConfigurationError, ConflictError, ConnectionError, Controller, ControllerRegistry, Cookies, Custom, CustomHttpError, DataConflictError, DataIntegrityError, DataMigrationError, DataNotFoundError, DataValidationError, DefaultValuePipe, Delete, DependencyError, DomainError, DuplicateEntityError, EntityNotFoundError, FeatureFlag, File, Files, ForbiddenError, GatewayTimeoutError, Get, Head, Header, Headers, Host, HttpError, HttpErrorWithDetails, HttpMethod, IndexingError, InfraAuthenticationError, InfraAuthorizationError, InfraCacheConnectionError, InfraConfigurationError, InfraDataDeserializationError, InfraDataSerializationError, InfraDatabaseConnectionError, InfraExternalServiceError, InfraMessageQueueError, InfraNetworkError, InfraServiceUnavailableError, InfraTimeoutError, InfrastructureError, InternalServerError, Ip, IpWhitelist, Locale, METADATA_KEYS, ManualRegister, MethodNotAllowedError, Module, Next, NotAcceptableError, NotFoundError, NotImplementedError, OperationNotAllowedError, Options, Owner, Param, ParamType, ParseArrayPipe, ParseBoolPipe, ParseDatePipe, ParseEnumPipe, ParseFilePipe, ParseFloatPipe, ParseIntPipe, ParseUUIDPipe, Patch, PayloadTooLargeError, Permissions, PersistenceError, PolicyBased, Post, PreconditionFailedError, ProxyAuthenticationRequiredError, Public, Put, Query, QueryExecutionError, RateLimit, Redirect, Repository, Req, ReqAuth, ReqContext, ReqPermissions, ReqUser, Request, RequestTimeoutError, Res, ResourceLimitError, ResourceLimitExceededError, Response, RestoreError, Roles, SchemaMismatchError, Scopes, Serialize, Service, ServiceUnavailableError, Session, SetMetadata, Throttle, TimeoutError, TooManyRequestsError, TransactionError, Transform, URITooLongError, UnauthorizedError, UnprocessableEntityError, UnsupportedMediaTypeError, UseGuards, UseInterceptors, UsePipes, Validate, ValidationError, ValidationPipe, isRedirectResponse };
|
|
1175
|
+
export { AllowAny, ApiKey, ApiResponse, BackupError, BadGatewayError, BadRequestError, Body, BusinessRuleViolationError, ConfigurationError, ConflictError, ConnectionError, Controller, ControllerRegistry, Cookies, Custom, CustomHttpError, DataConflictError, DataIntegrityError, DataMigrationError, DataNotFoundError, DataValidationError, DefaultValuePipe, Delete, DependencyError, DomainError, DuplicateEntityError, EntityNotFoundError, FeatureFlag, File, Files, ForbiddenError, GatewayTimeoutError, Get, Head, Header, Headers, Host, HttpError, HttpErrorWithDetails, HttpMethod, IndexingError, InfraAuthenticationError, InfraAuthorizationError, InfraCacheConnectionError, InfraConfigurationError, InfraDataDeserializationError, InfraDataSerializationError, InfraDatabaseConnectionError, InfraExternalServiceError, InfraMessageQueueError, InfraNetworkError, InfraServiceUnavailableError, InfraTimeoutError, InfrastructureError, InternalServerError, Ip, IpWhitelist, Locale, METADATA_KEYS, ManualRegister, MethodNotAllowedError, Module, Next, NotAcceptableError, NotFoundError, NotImplementedError, OperationNotAllowedError, Options, Owner, Param, ParamType, ParseArrayPipe, ParseBoolPipe, ParseDatePipe, ParseEnumPipe, ParseFilePipe, ParseFloatPipe, ParseIntPipe, ParseUUIDPipe, Patch, PayloadTooLargeError, Permissions, PersistenceError, PolicyBased, Post, PreconditionFailedError, ProxyAuthenticationRequiredError, Public, Put, Query, QueryExecutionError, RateLimit, Redirect, Repository, Req, ReqAuth, ReqContext, ReqPermissions, ReqUser, Request, RequestTimeoutError, Res, ResourceLimitError, ResourceLimitExceededError, Response, RestoreError, Roles, SchemaMismatchError, Scopes, Serialize, Service, ServiceUnavailableError, Session, SetMetadata, Throttle, TimeoutError, TooManyRequestsError, TransactionError, Transform, URITooLongError, UnauthorizedError, UnprocessableEntityError, UnsupportedMediaTypeError, UseGuards, UseInterceptors, UsePipes, Validate, ValidateDto, ValidationError, ValidationException, ValidationPipe, isRedirectResponse };
|
package/dist/hemia-common.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require('reflect-metadata');
|
|
4
4
|
var inversify = require('inversify');
|
|
5
|
+
var classValidator = require('class-validator');
|
|
6
|
+
var classTransformer = require('class-transformer');
|
|
5
7
|
|
|
6
8
|
const METADATA_KEYS = {
|
|
7
9
|
BASE_PATH: 'base_path',
|
|
@@ -380,22 +382,90 @@ function Custom(key) {
|
|
|
380
382
|
}
|
|
381
383
|
|
|
382
384
|
/**
|
|
383
|
-
* Decorador para validar datos de entrada
|
|
385
|
+
* Decorador para validar datos de entrada usando class-validator.
|
|
384
386
|
*
|
|
385
387
|
* @example
|
|
386
|
-
* // Valida el
|
|
387
|
-
* @Validate(
|
|
388
|
-
*
|
|
388
|
+
* // Valida el body usando un DTO
|
|
389
|
+
* async create(@Validate(CreateUserDto) @Body() body: CreateUserDto) {}
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* // Valida con función personalizada
|
|
393
|
+
* @Validate((value) => value.age > 18)
|
|
394
|
+
* async create(@Body('age') age: number) {}
|
|
389
395
|
*
|
|
390
|
-
* @param
|
|
396
|
+
* @param validatorOrDto Clase DTO o función de validación.
|
|
391
397
|
*/
|
|
392
|
-
function Validate(
|
|
398
|
+
function Validate(validatorOrDto) {
|
|
393
399
|
return (target, propertyKey, parameterIndex) => {
|
|
394
400
|
const existingValidators = Reflect.getMetadata(METADATA_KEYS.VALIDATORS, target, propertyKey || '') || [];
|
|
395
|
-
|
|
401
|
+
// Determinar si es una clase DTO o función
|
|
402
|
+
const isClassValidator = typeof validatorOrDto === 'function' && validatorOrDto.prototype;
|
|
403
|
+
const validator = isClassValidator
|
|
404
|
+
? async (value) => {
|
|
405
|
+
const instance = classTransformer.plainToInstance(validatorOrDto, value);
|
|
406
|
+
const errors = await classValidator.validate(instance, { whitelist: true, forbidNonWhitelisted: true });
|
|
407
|
+
if (errors.length > 0) {
|
|
408
|
+
throw new ValidationException(errors);
|
|
409
|
+
}
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
: validatorOrDto;
|
|
413
|
+
existingValidators.push({ index: parameterIndex, validator, dtoClass: isClassValidator ? validatorOrDto : null });
|
|
396
414
|
Reflect.defineMetadata(METADATA_KEYS.VALIDATORS, existingValidators, target, propertyKey || '');
|
|
397
415
|
};
|
|
398
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Decorador de método para validar todo el body con un DTO
|
|
419
|
+
* @example
|
|
420
|
+
* @Post()
|
|
421
|
+
* @ValidateDto(CreateUserDto)
|
|
422
|
+
* async create(@Body() body: CreateUserDto) {}
|
|
423
|
+
*/
|
|
424
|
+
function ValidateDto(dtoClass) {
|
|
425
|
+
return (target, propertyKey, descriptor) => {
|
|
426
|
+
const existingValidators = Reflect.getMetadata(METADATA_KEYS.VALIDATORS, target, propertyKey) || [];
|
|
427
|
+
const validator = async (value) => {
|
|
428
|
+
const instance = classTransformer.plainToInstance(dtoClass, value);
|
|
429
|
+
const errors = await classValidator.validate(instance, {
|
|
430
|
+
whitelist: true,
|
|
431
|
+
forbidNonWhitelisted: true
|
|
432
|
+
});
|
|
433
|
+
if (errors.length > 0) {
|
|
434
|
+
throw new ValidationException(errors);
|
|
435
|
+
}
|
|
436
|
+
return true;
|
|
437
|
+
};
|
|
438
|
+
const paramsMetadata = Reflect.getMetadata(METADATA_KEYS.PARAMS, target, propertyKey) || [];
|
|
439
|
+
const bodyParam = paramsMetadata.find((p) => p.type === exports.ParamType.BODY);
|
|
440
|
+
if (bodyParam) {
|
|
441
|
+
existingValidators.push({
|
|
442
|
+
index: bodyParam.index,
|
|
443
|
+
validator,
|
|
444
|
+
dtoClass
|
|
445
|
+
});
|
|
446
|
+
Reflect.defineMetadata(METADATA_KEYS.VALIDATORS, existingValidators, target, propertyKey);
|
|
447
|
+
}
|
|
448
|
+
return descriptor;
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Excepción personalizada para errores de validación
|
|
453
|
+
*/
|
|
454
|
+
class ValidationException extends Error {
|
|
455
|
+
constructor(errors) {
|
|
456
|
+
super('Validation failed');
|
|
457
|
+
this.statusCode = 400;
|
|
458
|
+
this.errors = errors;
|
|
459
|
+
this.name = 'ValidationException';
|
|
460
|
+
}
|
|
461
|
+
toJSON() {
|
|
462
|
+
return this.errors.map(error => ({
|
|
463
|
+
property: error.property,
|
|
464
|
+
value: error.value,
|
|
465
|
+
constraints: error.constraints
|
|
466
|
+
}));
|
|
467
|
+
}
|
|
468
|
+
}
|
|
399
469
|
|
|
400
470
|
/**
|
|
401
471
|
* Decorador para transformar datos de entrada en un controlador.
|
|
@@ -1229,6 +1299,8 @@ exports.UseGuards = UseGuards;
|
|
|
1229
1299
|
exports.UseInterceptors = UseInterceptors;
|
|
1230
1300
|
exports.UsePipes = UsePipes;
|
|
1231
1301
|
exports.Validate = Validate;
|
|
1302
|
+
exports.ValidateDto = ValidateDto;
|
|
1232
1303
|
exports.ValidationError = ValidationError;
|
|
1304
|
+
exports.ValidationException = ValidationException;
|
|
1233
1305
|
exports.ValidationPipe = ValidationPipe;
|
|
1234
1306
|
exports.isRedirectResponse = isRedirectResponse;
|
|
@@ -1,12 +1,40 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
+
import { ValidationError } from 'class-validator';
|
|
2
3
|
/**
|
|
3
|
-
* Decorador para validar datos de entrada
|
|
4
|
+
* Decorador para validar datos de entrada usando class-validator.
|
|
4
5
|
*
|
|
5
6
|
* @example
|
|
6
|
-
* // Valida el
|
|
7
|
-
* @Validate(
|
|
8
|
-
* async create(@Body() body: any) {}
|
|
7
|
+
* // Valida el body usando un DTO
|
|
8
|
+
* async create(@Validate(CreateUserDto) @Body() body: CreateUserDto) {}
|
|
9
9
|
*
|
|
10
|
-
* @
|
|
10
|
+
* @example
|
|
11
|
+
* // Valida con función personalizada
|
|
12
|
+
* @Validate((value) => value.age > 18)
|
|
13
|
+
* async create(@Body('age') age: number) {}
|
|
14
|
+
*
|
|
15
|
+
* @param validatorOrDto Clase DTO o función de validación.
|
|
16
|
+
*/
|
|
17
|
+
export declare function Validate(validatorOrDto: any): ParameterDecorator;
|
|
18
|
+
/**
|
|
19
|
+
* Decorador de método para validar todo el body con un DTO
|
|
20
|
+
* @example
|
|
21
|
+
* @Post()
|
|
22
|
+
* @ValidateDto(CreateUserDto)
|
|
23
|
+
* async create(@Body() body: CreateUserDto) {}
|
|
24
|
+
*/
|
|
25
|
+
export declare function ValidateDto(dtoClass: any): MethodDecorator;
|
|
26
|
+
/**
|
|
27
|
+
* Excepción personalizada para errores de validación
|
|
11
28
|
*/
|
|
12
|
-
export declare
|
|
29
|
+
export declare class ValidationException extends Error {
|
|
30
|
+
errors: ValidationError[];
|
|
31
|
+
statusCode: number;
|
|
32
|
+
constructor(errors: ValidationError[]);
|
|
33
|
+
toJSON(): {
|
|
34
|
+
property: string;
|
|
35
|
+
value: any;
|
|
36
|
+
constraints: {
|
|
37
|
+
[type: string]: string;
|
|
38
|
+
} | undefined;
|
|
39
|
+
}[];
|
|
40
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hemia/common",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "Paquete común para proyectos de Hemia",
|
|
5
5
|
"main": "dist/hemia-common.js",
|
|
6
6
|
"module": "dist/hemia-common.esm.js",
|
|
@@ -30,11 +30,17 @@
|
|
|
30
30
|
"ts-jest": "^29.2.5",
|
|
31
31
|
"ts-node": "^8.9.0",
|
|
32
32
|
"typescript": "^5.5.4",
|
|
33
|
-
"inversify": "^7.11.0"
|
|
33
|
+
"inversify": "^7.11.0",
|
|
34
|
+
"class-transformer": "^0.5.1",
|
|
35
|
+
"class-validator": "^0.15.1"
|
|
34
36
|
},
|
|
35
37
|
"peerDependencies": {
|
|
36
38
|
"reflect-metadata": "^0.2.2"
|
|
37
39
|
},
|
|
40
|
+
"optionalDependencies": {
|
|
41
|
+
"class-transformer": "^0.5.1",
|
|
42
|
+
"class-validator": "^0.15.1"
|
|
43
|
+
},
|
|
38
44
|
"author": "Hemia Team",
|
|
39
45
|
"license": "ISC",
|
|
40
46
|
"files": [
|