From ad10320f5f45dc281385c74d6dc643b5753c583a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Chevalier?= Date: Tue, 9 May 2023 17:28:18 +0200 Subject: [PATCH] WIP validation pipes --- src/modules/ad/ad.module.ts | 9 ++++++- .../ad/adapters/primaries/ad.controller.ts | 24 ++++++++++++++++++- src/modules/ad/adapters/primaries/ad.proto | 17 ++++++------- .../ad/domain/dtos/create-ad.request.ts | 19 +++++++++++---- src/modules/ad/domain/entities/ad.ts | 4 ++-- .../{addressEnum.ts => address.enum.ts} | 0 src/modules/ad/domain/entities/address.ts | 2 +- .../ad/domain/entities/frequency.enum.ts | 4 ++++ .../ad/domain/entities/frequencyEnum.ts | 4 ---- src/modules/ad/mappers/ad.profile.ts | 15 ++++++++++-- .../ad/tests/unit/create-ad.usecase.spec.ts | 8 +++---- .../secondaries/prisma-repository.abstract.ts | 1 + 12 files changed, 79 insertions(+), 28 deletions(-) rename src/modules/ad/domain/entities/{addressEnum.ts => address.enum.ts} (100%) create mode 100644 src/modules/ad/domain/entities/frequency.enum.ts delete mode 100644 src/modules/ad/domain/entities/frequencyEnum.ts diff --git a/src/modules/ad/ad.module.ts b/src/modules/ad/ad.module.ts index c51b8e1..324c0d6 100644 --- a/src/modules/ad/ad.module.ts +++ b/src/modules/ad/ad.module.ts @@ -8,6 +8,7 @@ import { AdProfile } from './mappers/ad.profile'; import { AdsRepository } from './adapters/secondaries/ads.repository'; import { Messager } from './adapters/secondaries/messager'; import { FindAdByUuidUseCase } from './domain/usecases/find-ad-by-uuid.usecase'; +import { CreateAdUseCase } from './domain/usecases/create-ad.usecase'; @Module({ imports: [ @@ -29,6 +30,12 @@ import { FindAdByUuidUseCase } from './domain/usecases/find-ad-by-uuid.usecase'; }), ], controllers: [AdController], - providers: [AdProfile, AdsRepository, Messager, FindAdByUuidUseCase], + providers: [ + AdProfile, + AdsRepository, + Messager, + FindAdByUuidUseCase, + CreateAdUseCase, + ], }) export class AdModule {} diff --git a/src/modules/ad/adapters/primaries/ad.controller.ts b/src/modules/ad/adapters/primaries/ad.controller.ts index 3b462a2..ec2ca65 100644 --- a/src/modules/ad/adapters/primaries/ad.controller.ts +++ b/src/modules/ad/adapters/primaries/ad.controller.ts @@ -1,13 +1,16 @@ import { Mapper } from '@automapper/core'; import { InjectMapper } from '@automapper/nestjs'; import { Controller, UsePipes } from '@nestjs/common'; -import { QueryBus } from '@nestjs/cqrs'; +import { CommandBus, QueryBus } from '@nestjs/cqrs'; import { GrpcMethod, RpcException } from '@nestjs/microservices'; import { RpcValidationPipe } from '../../../../utils/pipes/rpc.validation-pipe'; import { FindAdByUuidRequest } from '../../domain/dtos/find-ad-by-uuid.request'; import { AdPresenter } from './ad.presenter'; import { FindAdByUuidQuery } from '../../queries/find-ad-by-uuid.query'; import { Ad } from '../../domain/entities/ad'; +import { CreateAdRequest } from '../../domain/dtos/create-ad.request'; +import { CreateAdCommand } from '../../commands/create-ad.command'; +import { DatabaseException } from 'src/modules/database/exceptions/database.exception'; @UsePipes( new RpcValidationPipe({ @@ -18,6 +21,7 @@ import { Ad } from '../../domain/entities/ad'; @Controller() export class AdController { constructor( + private readonly _commandBus: CommandBus, private readonly queryBus: QueryBus, @InjectMapper() private readonly _mapper: Mapper, ) {} @@ -34,4 +38,22 @@ export class AdController { }); } } + + @GrpcMethod('AdsService', 'Create') + async createAd(data: CreateAdRequest): Promise { + try { + const ad = await this._commandBus.execute(new CreateAdCommand(data)); + return this._mapper.map(ad, Ad, AdPresenter); + } catch (e) { + if (e instanceof DatabaseException) { + if (e.message.includes('Already exists')) { + throw new RpcException({ + code: 6, + message: 'Ad already exists', + }); + } + } + throw new RpcException({}); + } + } } diff --git a/src/modules/ad/adapters/primaries/ad.proto b/src/modules/ad/adapters/primaries/ad.proto index 1b0faab..8bcf611 100644 --- a/src/modules/ad/adapters/primaries/ad.proto +++ b/src/modules/ad/adapters/primaries/ad.proto @@ -19,15 +19,15 @@ message Ad { string userUuid = 2; bool driver = 3; bool passenger = 4; - int32 frequency = 5; + Frequency frequency = 5; string fromDate = 6; string toDate = 7; Schedule schedule = 8; MarginDurations marginDurations = 9; - int32 seatsPassenger = 10; - int32 seatsDriver = 11; + optional int32 seatsPassenger = 10; + optional int32 seatsDriver = 11; bool strict = 12; - Addresses addresses = 13; + repeated Address addresses = 13; } message Schedule { @@ -50,10 +50,6 @@ message MarginDurations { int32 sun = 7; } -message Addresses { - repeated Address address = 1; -} - message Address { float lon = 1; float lat = 2; @@ -73,6 +69,11 @@ enum AddressType { OTHER = 5; } +enum Frequency { + PUNCTUAL = 1; + RECURRENT = 2; +} + message AdFilter { optional int32 page = 1; optional int32 perPage = 2; diff --git a/src/modules/ad/domain/dtos/create-ad.request.ts b/src/modules/ad/domain/dtos/create-ad.request.ts index c7808f4..44e7225 100644 --- a/src/modules/ad/domain/dtos/create-ad.request.ts +++ b/src/modules/ad/domain/dtos/create-ad.request.ts @@ -5,10 +5,12 @@ import { IsBoolean, IsDate, IsInt, - MinLength, ValidateIf, + ArrayMinSize, + IsArray, + IsEnum, } from 'class-validator'; -import { FrequencyType } from '../entities/frequencyEnum'; +import { Frequency } from '../entities/frequency.enum'; import { Address } from '../entities/address'; export class CreateAdRequest { @@ -22,21 +24,26 @@ export class CreateAdRequest { userUuid: string; @ValidateIf((ad) => (ad.passenger ? false : true)) + @IsOptional() @IsBoolean() @AutoMap() driver?: boolean; @ValidateIf((ad) => (ad.driver ? false : true)) + @IsOptional() @IsBoolean() @AutoMap() passenger?: boolean; @AutoMap() - frequency: FrequencyType; + @IsEnum(Frequency) + frequency: Frequency; + @IsDate() @AutoMap() fromDate: Date; + @IsDate() @AutoMap() toDate: Date; @@ -111,11 +118,13 @@ export class CreateAdRequest { sunMargin?: number; @ValidateIf((ad) => (ad.passenger ? false : true)) + @IsOptional() @IsInt() @AutoMap() seatsDriver?: number; @ValidateIf((ad) => (ad.driver ? false : true)) + @IsOptional() @IsInt() @AutoMap() seatsPassenger?: number; @@ -130,7 +139,7 @@ export class CreateAdRequest { @AutoMap() updatedAt?: Date; - @MinLength(2) - @AutoMap() + @IsArray() + @AutoMap(() => [Address]) addresses?: Array
; } diff --git a/src/modules/ad/domain/entities/ad.ts b/src/modules/ad/domain/entities/ad.ts index 87a5812..696895c 100644 --- a/src/modules/ad/domain/entities/ad.ts +++ b/src/modules/ad/domain/entities/ad.ts @@ -8,7 +8,7 @@ import { MinLength, ValidateIf, } from 'class-validator'; -import { FrequencyType } from '../entities/frequencyEnum'; +import { Frequency } from './frequency.enum'; import { Address } from '../entities/address'; export class Ad { @@ -31,7 +31,7 @@ export class Ad { passenger?: boolean; @AutoMap() - frequency: FrequencyType; + frequency: string; @AutoMap() fromDate: Date; diff --git a/src/modules/ad/domain/entities/addressEnum.ts b/src/modules/ad/domain/entities/address.enum.ts similarity index 100% rename from src/modules/ad/domain/entities/addressEnum.ts rename to src/modules/ad/domain/entities/address.enum.ts diff --git a/src/modules/ad/domain/entities/address.ts b/src/modules/ad/domain/entities/address.ts index e527215..22f6e0e 100644 --- a/src/modules/ad/domain/entities/address.ts +++ b/src/modules/ad/domain/entities/address.ts @@ -1,5 +1,5 @@ import { AutoMap } from '@automapper/classes'; -import { AddressType } from './addressEnum'; +import { AddressType } from './address.enum'; export class Address { @AutoMap() diff --git a/src/modules/ad/domain/entities/frequency.enum.ts b/src/modules/ad/domain/entities/frequency.enum.ts new file mode 100644 index 0000000..ec435e8 --- /dev/null +++ b/src/modules/ad/domain/entities/frequency.enum.ts @@ -0,0 +1,4 @@ +export enum Frequency { + PUNCTUAL = 1, + RECURRENT = 2, +} diff --git a/src/modules/ad/domain/entities/frequencyEnum.ts b/src/modules/ad/domain/entities/frequencyEnum.ts deleted file mode 100644 index 996c2c0..0000000 --- a/src/modules/ad/domain/entities/frequencyEnum.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum FrequencyType { - PUNCTUAL = 'PUNCTUAL', - RECURRENT = 'RECURRENT', -} diff --git a/src/modules/ad/mappers/ad.profile.ts b/src/modules/ad/mappers/ad.profile.ts index c501072..3f9b05e 100644 --- a/src/modules/ad/mappers/ad.profile.ts +++ b/src/modules/ad/mappers/ad.profile.ts @@ -1,9 +1,10 @@ -import { createMap, Mapper } from '@automapper/core'; +import { createMap, forMember, mapFrom, Mapper } from '@automapper/core'; import { AutomapperProfile, InjectMapper } from '@automapper/nestjs'; import { Injectable } from '@nestjs/common'; import { Ad } from '../domain/entities/ad'; import { AdPresenter } from '../adapters/primaries/ad.presenter'; import { CreateAdRequest } from '../domain/dtos/create-ad.request'; +import { Frequency } from '../domain/entities/frequency.enum'; @Injectable() export class AdProfile extends AutomapperProfile { @@ -14,7 +15,17 @@ export class AdProfile extends AutomapperProfile { override get profile() { return (mapper) => { createMap(mapper, Ad, AdPresenter); - createMap(mapper, CreateAdRequest, Ad); + createMap( + mapper, + CreateAdRequest, + Ad, + forMember( + (dest) => dest.frequency, + mapFrom((source) => + source.frequency == Frequency.PUNCTUAL ? 'PUNCTUAL' : 'RECCURENT', + ), + ), + ); }; } } diff --git a/src/modules/ad/tests/unit/create-ad.usecase.spec.ts b/src/modules/ad/tests/unit/create-ad.usecase.spec.ts index f045775..1102ad1 100644 --- a/src/modules/ad/tests/unit/create-ad.usecase.spec.ts +++ b/src/modules/ad/tests/unit/create-ad.usecase.spec.ts @@ -5,10 +5,10 @@ import { Address } from '../../domain/entities/address'; import { Messager } from '../../adapters/secondaries/messager'; import { AdsRepository } from '../../adapters/secondaries/ads.repository'; import { CreateAdCommand } from '../../commands/create-ad.command'; -import { AddressType } from '../../domain/entities/addressEnum'; +import { AddressType } from '../../domain/entities/address.enum'; import { AutomapperModule } from '@automapper/nestjs'; import { classes } from '@automapper/classes'; -import { FrequencyType } from '../../domain/entities/frequencyEnum'; +import { Frequency } from '../../domain/entities/frequency.enum'; import { Ad } from '../../domain/entities/ad'; import { AdProfile } from '../../mappers/ad.profile'; @@ -19,7 +19,7 @@ const mockAddress1: Address = { houseNumber: '5', street: 'Avenue Foch', locality: 'Nancy', - postalCode: '75000', + postalCode: '54000', country: 'France', type: AddressType.HOUSE_NUMBER, }; @@ -37,7 +37,7 @@ const newAdRequest: CreateAdRequest = { userUuid: '113e0000-0000-4000-a000-000000000000', driver: true, passenger: false, - frequency: FrequencyType.RECURRENT, + frequency: Frequency.RECURRENT, fromDate: new Date('01-05-2023'), toDate: new Date('20-08-2023'), tueTime: new Date(''), diff --git a/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts b/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts index dee142c..7346e3c 100644 --- a/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts +++ b/src/modules/database/adapters/secondaries/prisma-repository.abstract.ts @@ -88,6 +88,7 @@ export abstract class PrismaRepository implements IRepository { return res; } catch (e) { + console.log(e); if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name,