From 5fbc399924ba20ee8bca2a454051c973d584b327 Mon Sep 17 00:00:00 2001 From: sbriat Date: Thu, 4 May 2023 12:07:53 +0200 Subject: [PATCH] findAdByUuid, functional basic AdModule --- .gitlab-ci.yml | 54 ++++++++++++ ci/.env.ci | 15 ++++ ci/Dockerfile | 33 +++++++ ci/wait-up.sh | 12 +++ docker-compose.ci.service.yml | 19 ++++ docker-compose.ci.tools.yml | 26 ++++++ package-lock.json | 6 ++ package.json | 1 + .../migration.sql | 61 +++++++------ prisma/schema.prisma | 76 +++++++++------- src/app.module.ts | 3 + src/main.ts | 5 +- src/modules/ad/ad.module.ts | 32 ++++++- .../ad/adapters/primaries/ad.controller.ts | 38 ++++++++ .../ad/adapters/primaries/ad.presenter.ts | 9 ++ src/modules/ad/adapters/primaries/ad.proto | 86 +++++++++++++++++++ .../ad/adapters/secondaries/messager.ts | 18 ++++ .../ad/domain/dtos/find-ad-by-uuid.request.ts | 7 ++ src/modules/ad/domain/entities/ad.ts | 3 + .../ad/domain/interfaces/message-broker.ts | 12 +++ .../usecases/find-ad-by-uuid.usecase.ts | 31 +++++++ src/modules/ad/mappers/ad.profile.ts | 18 ++++ .../ad/queries/find-ad-by-uuid.query.ts | 9 ++ 23 files changed, 510 insertions(+), 64 deletions(-) create mode 100644 .gitlab-ci.yml create mode 100644 ci/.env.ci create mode 100644 ci/Dockerfile create mode 100644 ci/wait-up.sh create mode 100644 docker-compose.ci.service.yml create mode 100644 docker-compose.ci.tools.yml rename prisma/migrations/{20230504071417_init => 20230504100643_init}/migration.sql (50%) create mode 100644 src/modules/ad/adapters/primaries/ad.controller.ts create mode 100644 src/modules/ad/adapters/primaries/ad.presenter.ts create mode 100644 src/modules/ad/adapters/primaries/ad.proto create mode 100644 src/modules/ad/adapters/secondaries/messager.ts create mode 100644 src/modules/ad/domain/dtos/find-ad-by-uuid.request.ts create mode 100644 src/modules/ad/domain/interfaces/message-broker.ts create mode 100644 src/modules/ad/domain/usecases/find-ad-by-uuid.usecase.ts create mode 100644 src/modules/ad/mappers/ad.profile.ts create mode 100644 src/modules/ad/queries/find-ad-by-uuid.query.ts diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..be8ee32 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,54 @@ +image: docker:20.10.22 + +stages: + - test + - build + +############## +# TEST STAGE # +############## + +test: + stage: test + image: docker/compose:latest + variables: + DOCKER_TLS_CERTDIR: '' + services: + - docker:dind + script: + - docker-compose -f docker-compose.ci.tools.yml -p ad-tools --env-file ci/.env.ci up -d + - sh ci/wait-up.sh + - docker-compose -f docker-compose.ci.service.yml -p ad-service --env-file ci/.env.ci up -d + # - docker exec -t v3-ad-api sh -c "npm run test:integration:ci" + coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/ + rules: + - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_MESSAGE =~ /--check/ || $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + when: always + +############### +# BUILD STAGE # +############### + +build: + stage: build + image: docker:20.10.22 + variables: + DOCKER_TLS_CERTDIR: '' + services: + - docker:dind + before_script: + - echo -n $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY + script: + - export VERSION=$(docker run --rm -v "$PWD":/usr/src/app:ro -w /usr/src/app node:slim node -p "require('./package.json').version") + - docker pull $CI_REGISTRY_IMAGE:latest || true + - > + docker build + --pull + --cache-from $CI_REGISTRY_IMAGE:latest + --tag $CI_REGISTRY_IMAGE:$VERSION + --tag $CI_REGISTRY_IMAGE:latest + . + - docker push $CI_REGISTRY_IMAGE:$VERSION + - docker push $CI_REGISTRY_IMAGE:latest + only: + - main diff --git a/ci/.env.ci b/ci/.env.ci new file mode 100644 index 0000000..9c4e597 --- /dev/null +++ b/ci/.env.ci @@ -0,0 +1,15 @@ +# SERVICE +SERVICE_URL=0.0.0.0 +SERVICE_PORT=5006 + +# PRISMA +DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=public" + +# RABBIT MQ +RMQ_URI=amqp://v3-broker:5672 + +# MESSAGE BROKER +BROKER_IMAGE=rabbitmq:3-alpine + +# POSTGRES +POSTGRES_IMAGE=postgis/postgis:15-3.3 diff --git a/ci/Dockerfile b/ci/Dockerfile new file mode 100644 index 0000000..26b388a --- /dev/null +++ b/ci/Dockerfile @@ -0,0 +1,33 @@ +################### +# BUILD FOR CI TESTING +################### + +FROM node:18-alpine3.16 + +# Create app directory +WORKDIR /usr/src/app + +# A wildcard is used to ensure both package.json AND package-lock.json are copied +COPY package*.json ./ + +# Install app dependencies +RUN npm ci + +# Bundle app source +COPY . . + +# Generate prisma client +RUN npx prisma generate + +# Create a "dist" folder +RUN npm run build + +# Run unit tests +RUN npm run test:unit:ci + +# ESLint / Prettier +RUN npm run lint:check +RUN npm run pretty:check + +# Start the server +CMD [ "node", "dist/main.js" ] diff --git a/ci/wait-up.sh b/ci/wait-up.sh new file mode 100644 index 0000000..f29be44 --- /dev/null +++ b/ci/wait-up.sh @@ -0,0 +1,12 @@ +#!/bin/bash +testlog() { + docker logs v3-db | grep -q "database system is ready to accept connections" +} + +testlog 2> /dev/null +while [ $? -ne 0 ]; +do + sleep 5 + echo "Waiting for Test DB to be up..." + testlog 2> /dev/null +done diff --git a/docker-compose.ci.service.yml b/docker-compose.ci.service.yml new file mode 100644 index 0000000..d5e46af --- /dev/null +++ b/docker-compose.ci.service.yml @@ -0,0 +1,19 @@ +version: '3.8' + +services: + v3-ad-api: + container_name: v3-ad-api + build: + dockerfile: ci/Dockerfile + context: . + env_file: + - ci/.env.ci + ports: + - 5006:5006 + networks: + - v3-network + +networks: + v3-network: + name: v3-network + external: true diff --git a/docker-compose.ci.tools.yml b/docker-compose.ci.tools.yml new file mode 100644 index 0000000..a72f134 --- /dev/null +++ b/docker-compose.ci.tools.yml @@ -0,0 +1,26 @@ +version: '3.8' + +services: + db: + container_name: v3-db + image: ${POSTGRES_IMAGE} + environment: + POSTGRES_DB: mobicoop + POSTGRES_USER: mobicoop + POSTGRES_PASSWORD: mobicoop + ports: + - 5432:5432 + networks: + - v3-network + + broker: + container_name: v3-broker + image: ${BROKER_IMAGE} + ports: + - 5672:5672 + networks: + - v3-network + +networks: + v3-network: + name: v3-network diff --git a/package-lock.json b/package-lock.json index 502bcca..28256e6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@nestjs/platform-express": "^9.0.0", "@nestjs/terminus": "^9.2.2", "@prisma/client": "^4.13.0", + "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "ioredis": "^5.3.2", "reflect-metadata": "^0.1.13", @@ -3564,6 +3565,11 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, + "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==" + }, "node_modules/class-validator": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz", diff --git a/package.json b/package.json index c3ce7bd..6a51496 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@nestjs/platform-express": "^9.0.0", "@nestjs/terminus": "^9.2.2", "@prisma/client": "^4.13.0", + "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "ioredis": "^5.3.2", "reflect-metadata": "^0.1.13", diff --git a/prisma/migrations/20230504071417_init/migration.sql b/prisma/migrations/20230504100643_init/migration.sql similarity index 50% rename from prisma/migrations/20230504071417_init/migration.sql rename to prisma/migrations/20230504100643_init/migration.sql index 8a6f0b9..8430dc4 100644 --- a/prisma/migrations/20230504071417_init/migration.sql +++ b/prisma/migrations/20230504100643_init/migration.sql @@ -1,28 +1,34 @@ +-- CreateEnum +CREATE TYPE "Frequency" AS ENUM ('PUNCTUAL', 'RECURRENT'); + +-- CreateEnum +CREATE TYPE "AddressType" AS ENUM ('HOUSE_NUMBER', 'STREET_ADDRESS', 'LOCALITY', 'VENUE', 'OTHER'); + -- CreateTable CREATE TABLE "ad" ( "uuid" UUID NOT NULL, "userUuid" UUID NOT NULL, - "driver" BOOLEAN NOT NULL, - "passenger" BOOLEAN NOT NULL, - "frequency" INTEGER NOT NULL, + "driver" BOOLEAN, + "passenger" BOOLEAN, + "frequency" "Frequency" NOT NULL DEFAULT 'RECURRENT', "fromDate" DATE NOT NULL, - "toDate" DATE NOT NULL, - "monTime" TIMESTAMPTZ NOT NULL, - "tueTime" TIMESTAMPTZ NOT NULL, - "wedTime" TIMESTAMPTZ NOT NULL, - "thuTime" TIMESTAMPTZ NOT NULL, - "friTime" TIMESTAMPTZ NOT NULL, - "satTime" TIMESTAMPTZ NOT NULL, - "sunTime" TIMESTAMPTZ NOT NULL, - "monMargin" INTEGER NOT NULL, - "tueMargin" INTEGER NOT NULL, - "wedMargin" INTEGER NOT NULL, - "thuMargin" INTEGER NOT NULL, - "friMargin" INTEGER NOT NULL, - "satMargin" INTEGER NOT NULL, - "sunMargin" INTEGER NOT NULL, - "seatsDriver" SMALLINT NOT NULL, - "seatsPassenger" SMALLINT NOT NULL, + "toDate" DATE, + "monTime" TIMESTAMPTZ, + "tueTime" TIMESTAMPTZ, + "wedTime" TIMESTAMPTZ, + "thuTime" TIMESTAMPTZ, + "friTime" TIMESTAMPTZ, + "satTime" TIMESTAMPTZ, + "sunTime" TIMESTAMPTZ, + "monMargin" INTEGER, + "tueMargin" INTEGER, + "wedMargin" INTEGER, + "thuMargin" INTEGER, + "friMargin" INTEGER, + "satMargin" INTEGER, + "sunMargin" INTEGER, + "seatsDriver" SMALLINT, + "seatsPassenger" SMALLINT, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -33,14 +39,15 @@ CREATE TABLE "ad" ( CREATE TABLE "address" ( "uuid" UUID NOT NULL, "adUuid" UUID NOT NULL, + "position" SMALLINT NOT NULL, "lon" DOUBLE PRECISION NOT NULL, "lat" DOUBLE PRECISION NOT NULL, - "houseNumber" TEXT NOT NULL, - "street" TEXT NOT NULL, - "locality" TEXT NOT NULL, - "postalCode" TEXT NOT NULL, - "country" TEXT NOT NULL, - "type" SMALLINT NOT NULL, + "houseNumber" TEXT, + "street" TEXT, + "locality" TEXT, + "postalCode" TEXT, + "country" TEXT, + "type" "AddressType" DEFAULT 'OTHER', "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -60,4 +67,4 @@ CREATE INDEX "ad_fromDate_idx" ON "ad"("fromDate"); CREATE INDEX "ad_toDate_idx" ON "ad"("toDate"); -- AddForeignKey -ALTER TABLE "address" ADD CONSTRAINT "address_adUuid_fkey" FOREIGN KEY ("adUuid") REFERENCES "ad"("uuid") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "address" ADD CONSTRAINT "address_adUuid_fkey" FOREIGN KEY ("adUuid") REFERENCES "ad"("uuid") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index cab4ebb..da4f4e3 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -13,27 +13,27 @@ datasource db { model Ad { uuid String @id @default(uuid()) @db.Uuid userUuid String @db.Uuid - driver Boolean - passenger Boolean - frequency Int + driver Boolean? + passenger Boolean? + frequency Frequency @default(RECURRENT) fromDate DateTime @db.Date - toDate DateTime @db.Date - monTime DateTime @db.Timestamptz() - tueTime DateTime @db.Timestamptz() - wedTime DateTime @db.Timestamptz() - thuTime DateTime @db.Timestamptz() - friTime DateTime @db.Timestamptz() - satTime DateTime @db.Timestamptz() - sunTime DateTime @db.Timestamptz() - monMargin Int - tueMargin Int - wedMargin Int - thuMargin Int - friMargin Int - satMargin Int - sunMargin Int - seatsDriver Int @db.SmallInt - seatsPassenger Int @db.SmallInt + toDate DateTime? @db.Date + monTime DateTime? @db.Timestamptz() + tueTime DateTime? @db.Timestamptz() + wedTime DateTime? @db.Timestamptz() + thuTime DateTime? @db.Timestamptz() + friTime DateTime? @db.Timestamptz() + satTime DateTime? @db.Timestamptz() + sunTime DateTime? @db.Timestamptz() + monMargin Int? + tueMargin Int? + wedMargin Int? + thuMargin Int? + friMargin Int? + satMargin Int? + sunMargin Int? + seatsDriver Int? @db.SmallInt + seatsPassenger Int? @db.SmallInt createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt addresses Address[] @@ -46,19 +46,33 @@ model Ad { } model Address { - uuid String @id @default(uuid()) @db.Uuid - adUuid String @db.Uuid + uuid String @id @default(uuid()) @db.Uuid + adUuid String @db.Uuid + position Int @db.SmallInt lon Float lat Float - houseNumber String - street String - locality String - postalCode String - country String - type Int @db.SmallInt - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - Ad Ad @relation(fields: [adUuid], references: [uuid]) + houseNumber String? + street String? + locality String? + postalCode String? + country String? + type AddressType? @default(OTHER) + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt + Ad Ad @relation(fields: [adUuid], references: [uuid], onDelete: Cascade) @@map("address") } + +enum Frequency { + PUNCTUAL + RECURRENT +} + +enum AddressType { + HOUSE_NUMBER + STREET_ADDRESS + LOCALITY + VENUE + OTHER +} diff --git a/src/app.module.ts b/src/app.module.ts index a8fb088..53070e4 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -3,10 +3,13 @@ import { ConfigModule } from '@nestjs/config'; import { ConfigurationModule } from './modules/configuration/configuration.module'; import { HealthModule } from './modules/health/health.module'; import { AdModule } from './modules/ad/ad.module'; +import { AutomapperModule } from '@automapper/nestjs'; +import { classes } from '@automapper/classes'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), + AutomapperModule.forRoot({ strategyInitializer: classes() }), ConfigurationModule, HealthModule, AdModule, diff --git a/src/main.ts b/src/main.ts index 3b27143..1303641 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,10 +11,9 @@ async function bootstrap() { app.connectMicroservice({ transport: Transport.GRPC, options: { - // package: ['ad', 'health'], - package: ['health'], + package: ['ad', 'health'], protoPath: [ - // join(__dirname, 'modules/ad/adapters/primaries/ad.proto'), + join(__dirname, 'modules/ad/adapters/primaries/ad.proto'), join(__dirname, 'modules/health/adapters/primaries/health.proto'), ], url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT, diff --git a/src/modules/ad/ad.module.ts b/src/modules/ad/ad.module.ts index 8946839..c51b8e1 100644 --- a/src/modules/ad/ad.module.ts +++ b/src/modules/ad/ad.module.ts @@ -1,8 +1,34 @@ import { Module } from '@nestjs/common'; +import { AdController } from './adapters/primaries/ad.controller'; +import { DatabaseModule } from '../database/database.module'; +import { CqrsModule } from '@nestjs/cqrs'; +import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +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'; @Module({ - imports: [], - controllers: [], - providers: [], + imports: [ + DatabaseModule, + CqrsModule, + RabbitMQModule.forRootAsync(RabbitMQModule, { + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + exchanges: [ + { + name: configService.get('RMQ_EXCHANGE'), + type: 'topic', + }, + ], + uri: configService.get('RMQ_URI'), + connectionInitOptions: { wait: false }, + }), + inject: [ConfigService], + }), + ], + controllers: [AdController], + providers: [AdProfile, AdsRepository, Messager, FindAdByUuidUseCase], }) export class AdModule {} diff --git a/src/modules/ad/adapters/primaries/ad.controller.ts b/src/modules/ad/adapters/primaries/ad.controller.ts new file mode 100644 index 0000000..a53df3c --- /dev/null +++ b/src/modules/ad/adapters/primaries/ad.controller.ts @@ -0,0 +1,38 @@ +import { Mapper } from '@automapper/core'; +import { InjectMapper } from '@automapper/nestjs'; +import { Controller, UsePipes } from '@nestjs/common'; +import { 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'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: false, + forbidUnknownValues: false, + }), +) +@Controller() +export class AdController { + constructor( + private readonly queryBus: QueryBus, + @InjectMapper() private readonly _mapper: Mapper, + ) {} + + @GrpcMethod('AdsService', 'FindOneByUuid') + async findOnebyUuid(data: FindAdByUuidRequest): Promise { + try { + console.log('ici'); + const ad = await this.queryBus.execute(new FindAdByUuidQuery(data)); + return this._mapper.map(ad, Ad, AdPresenter); + } catch (e) { + throw new RpcException({ + code: e.code, + message: e.message, + }); + } + } +} diff --git a/src/modules/ad/adapters/primaries/ad.presenter.ts b/src/modules/ad/adapters/primaries/ad.presenter.ts new file mode 100644 index 0000000..b4e58e4 --- /dev/null +++ b/src/modules/ad/adapters/primaries/ad.presenter.ts @@ -0,0 +1,9 @@ +import { AutoMap } from '@automapper/classes'; + +export class AdPresenter { + @AutoMap() + uuid: string; + + @AutoMap() + driver: boolean; +} diff --git a/src/modules/ad/adapters/primaries/ad.proto b/src/modules/ad/adapters/primaries/ad.proto new file mode 100644 index 0000000..1b0faab --- /dev/null +++ b/src/modules/ad/adapters/primaries/ad.proto @@ -0,0 +1,86 @@ +syntax = "proto3"; + +package ad; + +service AdsService { + rpc FindOneByUuid(AdByUuid) returns (Ad); + rpc FindAll(AdFilter) returns (Ads); + rpc Create(Ad) returns (Ad); + rpc Update(Ad) returns (Ad); + rpc Delete(AdByUuid) returns (Empty); +} + +message AdByUuid { + string uuid = 1; +} + +message Ad { + string uuid = 1; + string userUuid = 2; + bool driver = 3; + bool passenger = 4; + int32 frequency = 5; + string fromDate = 6; + string toDate = 7; + Schedule schedule = 8; + MarginDurations marginDurations = 9; + int32 seatsPassenger = 10; + int32 seatsDriver = 11; + bool strict = 12; + Addresses addresses = 13; +} + +message Schedule { + string mon = 1; + string tue = 2; + string wed = 3; + string thu = 4; + string fri = 5; + string sat = 6; + string sun = 7; +} + +message MarginDurations { + int32 mon = 1; + int32 tue = 2; + int32 wed = 3; + int32 thu = 4; + int32 fri = 5; + int32 sat = 6; + int32 sun = 7; +} + +message Addresses { + repeated Address address = 1; +} + +message Address { + float lon = 1; + float lat = 2; + string houseNumber = 3; + string street = 4; + string locality = 5; + string postalCode = 6; + string country = 7; + AddressType type = 8; +} + +enum AddressType { + HOUSE_NUMBER = 1; + STREET_ADDRESS = 2; + LOCALITY = 3; + VENUE = 4; + OTHER = 5; +} + +message AdFilter { + optional int32 page = 1; + optional int32 perPage = 2; +} + +message Ads { + repeated Ad data = 1; + int32 total = 2; +} + +message Empty {} diff --git a/src/modules/ad/adapters/secondaries/messager.ts b/src/modules/ad/adapters/secondaries/messager.ts new file mode 100644 index 0000000..0ee32e3 --- /dev/null +++ b/src/modules/ad/adapters/secondaries/messager.ts @@ -0,0 +1,18 @@ +import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { IMessageBroker } from '../../domain/interfaces/message-broker'; + +@Injectable() +export class Messager extends IMessageBroker { + constructor( + private readonly _amqpConnection: AmqpConnection, + configService: ConfigService, + ) { + super(configService.get('RMQ_EXCHANGE')); + } + + publish(routingKey: string, message: string): void { + this._amqpConnection.publish(this.exchange, routingKey, message); + } +} diff --git a/src/modules/ad/domain/dtos/find-ad-by-uuid.request.ts b/src/modules/ad/domain/dtos/find-ad-by-uuid.request.ts new file mode 100644 index 0000000..9741962 --- /dev/null +++ b/src/modules/ad/domain/dtos/find-ad-by-uuid.request.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class FindAdByUuidRequest { + @IsString() + @IsNotEmpty() + uuid: string; +} diff --git a/src/modules/ad/domain/entities/ad.ts b/src/modules/ad/domain/entities/ad.ts index 0350f1a..b4e95e0 100644 --- a/src/modules/ad/domain/entities/ad.ts +++ b/src/modules/ad/domain/entities/ad.ts @@ -3,4 +3,7 @@ import { AutoMap } from '@automapper/classes'; export class Ad { @AutoMap() uuid: string; + + @AutoMap() + driver: boolean; } diff --git a/src/modules/ad/domain/interfaces/message-broker.ts b/src/modules/ad/domain/interfaces/message-broker.ts new file mode 100644 index 0000000..594aa43 --- /dev/null +++ b/src/modules/ad/domain/interfaces/message-broker.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export abstract class IMessageBroker { + exchange: string; + + constructor(exchange: string) { + this.exchange = exchange; + } + + abstract publish(routingKey: string, message: string): void; +} diff --git a/src/modules/ad/domain/usecases/find-ad-by-uuid.usecase.ts b/src/modules/ad/domain/usecases/find-ad-by-uuid.usecase.ts new file mode 100644 index 0000000..719cc83 --- /dev/null +++ b/src/modules/ad/domain/usecases/find-ad-by-uuid.usecase.ts @@ -0,0 +1,31 @@ +import { NotFoundException } from '@nestjs/common'; +import { QueryHandler } from '@nestjs/cqrs'; +import { FindAdByUuidQuery } from '../../queries/find-ad-by-uuid.query'; +import { AdsRepository } from '../../adapters/secondaries/ads.repository'; +import { Messager } from '../../adapters/secondaries/messager'; +import { Ad } from '../entities/ad'; + +@QueryHandler(FindAdByUuidQuery) +export class FindAdByUuidUseCase { + constructor( + private readonly repository: AdsRepository, + private readonly messager: Messager, + ) {} + + async execute(findAdByUuid: FindAdByUuidQuery): Promise { + try { + const ad = await this.repository.findOneByUuid(findAdByUuid.uuid); + if (!ad) throw new NotFoundException(); + return ad; + } catch (error) { + this.messager.publish( + 'logging.ad.read.warning', + JSON.stringify({ + query: findAdByUuid, + error, + }), + ); + throw error; + } + } +} diff --git a/src/modules/ad/mappers/ad.profile.ts b/src/modules/ad/mappers/ad.profile.ts new file mode 100644 index 0000000..f0ddffe --- /dev/null +++ b/src/modules/ad/mappers/ad.profile.ts @@ -0,0 +1,18 @@ +import { createMap, 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'; + +@Injectable() +export class AdProfile extends AutomapperProfile { + constructor(@InjectMapper() mapper: Mapper) { + super(mapper); + } + + override get profile() { + return (mapper) => { + createMap(mapper, Ad, AdPresenter); + }; + } +} diff --git a/src/modules/ad/queries/find-ad-by-uuid.query.ts b/src/modules/ad/queries/find-ad-by-uuid.query.ts new file mode 100644 index 0000000..32e0257 --- /dev/null +++ b/src/modules/ad/queries/find-ad-by-uuid.query.ts @@ -0,0 +1,9 @@ +import { FindAdByUuidRequest } from '../domain/dtos/find-ad-by-uuid.request'; + +export class FindAdByUuidQuery { + readonly uuid: string; + + constructor(findAdByUuidRequest: FindAdByUuidRequest) { + this.uuid = findAdByUuidRequest.uuid; + } +}