Skip to content

Commit

Permalink
Merge branch 'main' into feat/crud-dto-only
Browse files Browse the repository at this point in the history
  • Loading branch information
alexpryazhnikov authored Aug 27, 2024
2 parents da631cb + 253b35d commit 42b3fe9
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 153 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@steroidsjs/nest",
"version": "2.1.1",
"version": "2.2.0",
"scripts": {
"script": "nps",
"watch": "nps watch",
Expand Down
4 changes: 2 additions & 2 deletions src/infrastructure/applications/rest/config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {APP_FILTER} from '@nestjs/core';
import baseConfig from '../base/config';
import {IRestAppModuleConfig} from './IRestAppModuleConfig';
import {ValidationExceptionFilterCustom} from './filters/ValidationExceptionFilterCustom';
import {RequestExecutionExceptionFilter} from './filters/RequestExecutionExceptionFilter';
import {GracefulController} from "./graceful/GracefulController";
import {GracefulService} from "./graceful/GracefulService";
import {ValidationExceptionFilter} from '../../filters/ValidationExceptionFilter';

export default {
...baseConfig,
Expand All @@ -13,7 +13,7 @@ export default {
providers: [
{
provide: APP_FILTER,
useClass: ValidationExceptionFilterCustom,
useClass: ValidationExceptionFilter,
},
{
provide: APP_FILTER,
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion src/infrastructure/decorators/fields/DecimalNumberField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function isDecimalNumber(value: unknown, options?: IDecimalFieldOptions):
if (typeof value !== 'number') { return false; }

return isDecimal(value.toString(), {
decimal_digits: '0,' + options.scale ?? '',
decimal_digits: '0,' + (options.scale ?? ''),
});
}

Expand Down
27 changes: 24 additions & 3 deletions src/infrastructure/decorators/fields/TextField.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import {applyDecorators} from '@nestjs/common';
import {Column} from '@steroidsjs/typeorm';
import {toInteger as _toInteger} from 'lodash';
import {IsOptional, IsString, MaxLength, MinLength} from 'class-validator';
import {BaseField, IBaseFieldOptions} from './BaseField';

export function TextField(options: IBaseFieldOptions = {}) {
return applyDecorators(
export interface ITextFieldOptions extends IBaseFieldOptions {
isStringConstraintMessage?: string,
minConstraintMessage?: string,
maxConstraintMessage?: string,
}

export function TextField(options: ITextFieldOptions = {}) {
return applyDecorators(...[
BaseField(options, {
decoratorName: 'TextField',
appType: 'text',
Expand All @@ -15,5 +23,18 @@ export function TextField(options: IBaseFieldOptions = {}) {
default: options.defaultValue,
nullable: options.nullable,
}),
);
IsString({
each: options.isArray,
message: options.isStringConstraintMessage || 'Должна быть строка',
}),
!options.required && IsOptional(),
typeof options.min === 'number' && MinLength(options.min, {
message: `Длина строка должна быть не менее ${options.min}` || options.minConstraintMessage,
each: options.isArray,
}),
typeof options.max === 'number' && MaxLength(_toInteger(options.max), {
message: `Длина строка должна быть не более ${options.max}` || options.maxConstraintMessage,
each: options.isArray,
}),
].filter(Boolean));
}
24 changes: 2 additions & 22 deletions src/infrastructure/filters/ValidationExceptionFilter.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,19 @@
import {ExceptionFilter, Catch, ArgumentsHost, HttpStatus} from '@nestjs/common';
import { Response } from 'express';
import {ValidationError} from 'class-validator';
import {ValidationException} from '../../usecases/exceptions/ValidationException';
import {ValidationException} from '../../usecases/exceptions';

@Catch(ValidationException)
export class ValidationExceptionFilter implements ExceptionFilter {
parseErrors(errors) {
if (Array.isArray(errors) && errors[0] instanceof ValidationError) {
return errors.reduce(
(result: any, item) => {
if (item.constraints) {
result[item.property] = [].concat(Object.values(item.constraints));
}
if (item.children?.length > 0) {
result[item.property] = this.parseErrors(item.children);
}
return result;
},
{},
);
}
return errors;
}

catch(exception: ValidationException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const errors = this.parseErrors(exception.errors);

response
// TODO#Warning - ставим код 200, чтобы форма на фронте принимала массив ошибок
.status(HttpStatus.OK)
.json({
statusCode: HttpStatus.BAD_REQUEST,
errors,
errors: exception.errors,
});
}
}
2 changes: 1 addition & 1 deletion src/infrastructure/repositories/CrudRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export class CrudRepository<TModel> implements ICrudRepository<TModel>, OnModule
throw new Error('Property modelClass is not set in repository: ' + this.constructor.name);
}

return DataMapper.create(this.modelClass, obj, TRANSFORM_TYPE_FROM_DB, true);
return DataMapper.create<TModel>(this.modelClass, obj as Partial<TModel>, TRANSFORM_TYPE_FROM_DB, true);
}

/**
Expand Down
7 changes: 3 additions & 4 deletions src/usecases/exceptions/ValidationException.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {ValidationError} from 'class-validator';
import {IFormError} from '../interfaces/IFormError';
import {IErrorsCompositeObject} from '../interfaces/IErrorsCompositeObject';

export class ValidationException {
public errors;
public errors: IErrorsCompositeObject;

constructor(errors: ValidationError[] | IFormError) {
constructor(errors: IErrorsCompositeObject) {
this.errors = errors;
}
}
29 changes: 26 additions & 3 deletions src/usecases/helpers/DataMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {isObject as _isObject} from 'lodash';
import {getFieldOptions, getMetaFields, isMetaClass} from '../../infrastructure/decorators/fields/BaseField';
import {IRelationFieldOptions} from '../../infrastructure/decorators/fields/RelationField';
import {DECORATORS} from '@nestjs/swagger/dist/constants';
import {DeepPartial} from '@steroidsjs/typeorm';
import {
getTransformCallbacks,
ITransformType,
Expand All @@ -11,15 +12,37 @@ import {
} from '../../infrastructure/decorators/Transform';
import {getModelBuilder} from '../../infrastructure/decorators/TableFromModel';
import {IType} from '../interfaces/IType';
import {DeepPartial} from "@steroidsjs/typeorm";

export class DataMapper {

static create<T>(MetaClass: IType<T>, values: DeepPartial<T>, transformType: ITransformType = TRANSFORM_TYPE_DEFAULT, skipBuilder = false): T {
static create<T>(
MetaClass: IType<T>,
values: Array<DeepPartial<T> | Partial<T>>,
transformType?: ITransformType,
skipBuilder?: boolean,
): T[];

static create<T>(
MetaClass: IType<T>,
values: DeepPartial<T> | Partial<T>,
transformType?: ITransformType,
skipBuilder?: boolean,
): T;

static create<T>(
MetaClass: IType<T>,
values: DeepPartial<T> | DeepPartial<T>[],
transformType: ITransformType = TRANSFORM_TYPE_DEFAULT,
skipBuilder = false,
): T | T[] {
// Check empty
if (values === null) {
return null;
}
if (Array.isArray(values)) {
return values.map((value) => (
this.create(MetaClass, value, transformType, skipBuilder)
));
}

const builder = !skipBuilder && getModelBuilder(MetaClass);
if (builder) {
Expand Down
Loading

0 comments on commit 42b3fe9

Please sign in to comment.