From e3bc7234abcb464e02339e3e637d3a2a4627c8fe Mon Sep 17 00:00:00 2001 From: sbriat Date: Tue, 6 Jun 2023 12:53:59 +0200 Subject: [PATCH] upgrade health, use configuration and broker packages --- .env.dist | 10 +- README.md | 115 ++++++++++++-- package-lock.json | 148 +++++++++++++++++- package.json | 6 +- src/app.constants.ts | 2 + src/app.module.ts | 55 ++++++- src/interfaces/message-publisher.ts | 3 + src/modules/ad/ad.constants.ts | 1 + src/modules/ad/ad.module.ts | 37 ++--- .../adapters/secondaries/message-publisher.ts | 16 ++ .../ad/adapters/secondaries/messager.ts | 18 --- .../ad/domain/interfaces/message-broker.ts | 12 -- .../ad/domain/usecases/create-ad.usecase.ts | 15 +- .../usecases/find-ad-by-uuid.usecase.ts | 10 +- .../secondaries/message-publisher.spec.ts | 36 +++++ .../adapters/secondaries/messager.spec.ts | 47 ------ .../unit/domain/create-ad.usecase.spec.ts | 13 +- .../domain/find-ad-by-uuid.usecase.spec.ts | 8 +- .../configuration-messager.controller.ts | 77 --------- .../redis-configuration.repository.ts | 23 --- .../commands/delete-configuration.command.ts | 9 -- .../commands/set-configuration.command.ts | 9 -- .../configuration/configuration.module.ts | 68 -------- .../dtos/delete-configuration.request.ts | 11 -- .../domain/dtos/set-configuration.request.ts | 15 -- .../domain/entities/configuration.ts | 12 -- .../interfaces/configuration.repository.ts | 8 - .../usecases/delete-configuration.usecase.ts | 16 -- .../usecases/get-configuration.usecase.ts | 14 -- .../usecases/set-configuration.usecase.ts | 17 -- .../queries/get-configuration.query.ts | 9 -- .../unit/delete-configuration.usecase.spec.ts | 49 ------ .../unit/get-configuration.usecase.spec.ts | 43 ----- .../redis-configuration.repository.spec.ts | 47 ------ .../unit/set-configuration.usecase.spec.ts | 50 ------ .../primaries/health-server.controller.ts | 10 +- .../adapters/primaries/health.controller.ts | 21 +-- .../adapters/secondaries/message-broker.ts | 12 -- .../adapters/secondaries/message-publisher.ts | 16 ++ .../health/adapters/secondaries/messager.ts | 18 --- .../interfaces/check-repository.interface.ts | 3 + .../prisma.health-indicator.usecase.ts | 25 --- .../repositories.health-indicator.usecase.ts | 33 ++++ src/modules/health/health.module.ts | 40 ++--- .../tests/unit/message-publisher.spec.ts | 36 +++++ .../health/tests/unit/messager.spec.ts | 47 ------ ...sitories.health-indicator.usecase.spec.ts} | 29 ++-- 47 files changed, 540 insertions(+), 779 deletions(-) create mode 100644 src/app.constants.ts create mode 100644 src/interfaces/message-publisher.ts create mode 100644 src/modules/ad/ad.constants.ts create mode 100644 src/modules/ad/adapters/secondaries/message-publisher.ts delete mode 100644 src/modules/ad/adapters/secondaries/messager.ts delete mode 100644 src/modules/ad/domain/interfaces/message-broker.ts create mode 100644 src/modules/ad/tests/unit/adapters/secondaries/message-publisher.spec.ts delete mode 100644 src/modules/ad/tests/unit/adapters/secondaries/messager.spec.ts delete mode 100644 src/modules/configuration/adapters/primaries/configuration-messager.controller.ts delete mode 100644 src/modules/configuration/adapters/secondaries/redis-configuration.repository.ts delete mode 100644 src/modules/configuration/commands/delete-configuration.command.ts delete mode 100644 src/modules/configuration/commands/set-configuration.command.ts delete mode 100644 src/modules/configuration/configuration.module.ts delete mode 100644 src/modules/configuration/domain/dtos/delete-configuration.request.ts delete mode 100644 src/modules/configuration/domain/dtos/set-configuration.request.ts delete mode 100644 src/modules/configuration/domain/entities/configuration.ts delete mode 100644 src/modules/configuration/domain/interfaces/configuration.repository.ts delete mode 100644 src/modules/configuration/domain/usecases/delete-configuration.usecase.ts delete mode 100644 src/modules/configuration/domain/usecases/get-configuration.usecase.ts delete mode 100644 src/modules/configuration/domain/usecases/set-configuration.usecase.ts delete mode 100644 src/modules/configuration/queries/get-configuration.query.ts delete mode 100644 src/modules/configuration/tests/unit/delete-configuration.usecase.spec.ts delete mode 100644 src/modules/configuration/tests/unit/get-configuration.usecase.spec.ts delete mode 100644 src/modules/configuration/tests/unit/redis-configuration.repository.spec.ts delete mode 100644 src/modules/configuration/tests/unit/set-configuration.usecase.spec.ts delete mode 100644 src/modules/health/adapters/secondaries/message-broker.ts create mode 100644 src/modules/health/adapters/secondaries/message-publisher.ts delete mode 100644 src/modules/health/adapters/secondaries/messager.ts create mode 100644 src/modules/health/domain/interfaces/check-repository.interface.ts delete mode 100644 src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts create mode 100644 src/modules/health/domain/usecases/repositories.health-indicator.usecase.ts create mode 100644 src/modules/health/tests/unit/message-publisher.spec.ts delete mode 100644 src/modules/health/tests/unit/messager.spec.ts rename src/modules/health/tests/unit/{prisma.health-indicator.usecase.spec.ts => repositories.health-indicator.usecase.spec.ts} (54%) diff --git a/.env.dist b/.env.dist index 50f85dd..2e4c53b 100644 --- a/.env.dist +++ b/.env.dist @@ -7,9 +7,9 @@ HEALTH_SERVICE_PORT=6006 # PRISMA DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=ad" -# RABBIT MQ -RMQ_URI=amqp://v3-broker:5672 -RMQ_EXCHANGE=mobicoop +# MESSAGE BROKER +MESSAGE_BROKER_URI=amqp://v3-broker:5672 +MESSAGE_BROKER_EXCHANGE=mobicoop # REDIS REDIS_HOST=v3-redis @@ -26,5 +26,5 @@ ROLE=passenger SEATS_PROVIDED=3 SEATS_REQUESTED=1 -# ACCEPT ONLY SAME FREQUENCY REQUESTS -STRICT_FREQUENCY=false \ No newline at end of file +# ACCEPT ONLY SAME FREQUENCY REQUESTS +STRICT_FREQUENCY=false diff --git a/README.md b/README.md index ea3c8d4..92c67cb 100644 --- a/README.md +++ b/README.md @@ -58,22 +58,18 @@ The app exposes the following [gRPC](https://grpc.io/) services : - **Create** : create an ad (note that uuid is optional, a uuid will be automatically attributed if it is not provided) + Punctual driver ad : + ```json { - "userUuid": "113e0000-0000-4000-a000-000000000000", + "userUuid": "80c9bb02-0931-4a1d-bea6-22d358992245", "driver": true, - "passenger": false, - "frequency": "PUNCTUAL", - "departure": "2023-01-15", - "toDate": "2023-08-01", - - "marginDurations": { - "mon": 800 - }, - "seatsPassenger": 0, "seatsDriver": 3, + "frequency": "PUNCTUAL", + "departure": "2023-01-15 09:00", "addresses": [ { + "position": 0, "lon": 48.68944505415954, "lat": 6.176510296462267, "houseNumber": "5", @@ -83,6 +79,7 @@ The app exposes the following [gRPC](https://grpc.io/) services : "country": "France" }, { + "position": 1, "lon": 48.8566, "lat": 2.3522, "locality": "Paris", @@ -93,6 +90,104 @@ The app exposes the following [gRPC](https://grpc.io/) services : } ``` + Punctual driver and passenger ad : + + ```json + { + "userUuid": "80c9bb02-0931-4a1d-bea6-22d358992245", + "driver": true, + "pasenger": true, + "seatsDriver": 3, + "seatsPassenger": 1, + "frequency": "PUNCTUAL", + "departure": "2023-01-15 09:00", + "addresses": [ + { + "position": 0, + "lon": 48.68944505415954, + "lat": 6.176510296462267, + "houseNumber": "5", + "street": "Avenue Foch", + "locality": "Nancy", + "postalCode": "54000", + "country": "France" + }, + { + "position": 1, + "lon": 48.8566, + "lat": 2.3522, + "locality": "Paris", + "postalCode": "75000", + "country": "France" + } + ] + } + ``` + + Recurrent passenger ad : + + ```json + { + "userUuid": "80c9bb02-0931-4a1d-bea6-22d358992245", + "passenger": true, + "seatsPassenger": 1, + "frequency": "RECURRRENT", + "fromDate": "2023-01-15", + "toDate": "2023-12-31", + "schedule": { + "mon": "07:00", + "tue": "07:05", + "fri": "07:10" + }, + "addresses": [ + { + "position": 0, + "lon": 48.68944505415954, + "lat": 6.176510296462267, + "houseNumber": "5", + "street": "Avenue Foch", + "locality": "Nancy", + "postalCode": "54000", + "country": "France" + }, + { + "position": 1, + "lon": 48.8566, + "lat": 2.3522, + "locality": "Paris", + "postalCode": "75000", + "country": "France" + } + ] + } + ``` + + The list of possible options when creating an ad : + + - uuid (optional): the uuid of the ad + - userUuid: the user uuid + - driver (boolean, optional): if the ad is a driver ad + - passenger (boolean, optional): if the ad is a passenger ad + - frequency: `PUNCTUAL` or `RECURRENT` + - departure (required if punctual): departure date and hour/minute for a punctual ad + - fromDate (required if recurrent): start date for recurrent ad + - toDate (required if recurrent): end date for recurrent ad + - schedule (required if recurrent): an object with the departure time for each carpooled day in the week + - marginDurations (optional): an object with the margin duration (in seconds) for each carpooled day in the week, eg: + + { + "mon": 900, + "tue": 850, + "fri": 950 + } + + - seatsDriver (optional): number of seats proposed as driver; + - seatsPassenger (optional): number of seats requested as passenger; + - strict (boolean, optional): if set to true, allow matching only with similar frequency ads + - addresses: an array of adresses that represent the waypoints of the journey (only first and last waypoints are used for passenger ads) + + Default values must be set in `.env` file. + ## Messages As mentionned earlier, RabbitMQ messages are sent after these events : diff --git a/package-lock.json b/package-lock.json index ba693c1..2d720dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,11 @@ "@automapper/classes": "^8.7.7", "@automapper/core": "^8.7.7", "@automapper/nestjs": "^8.7.7", - "@golevelup/nestjs-rabbitmq": "^3.6.0", "@grpc/grpc-js": "^1.8.14", "@grpc/proto-loader": "^0.7.6", "@liaoliaots/nestjs-redis": "^9.0.5", + "@mobicoop/configuration-module": "^1.1.0", + "@mobicoop/message-broker-module": "^1.0.5", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.3.1", "@nestjs/core": "^9.0.0", @@ -56,6 +57,24 @@ "typescript": "^4.7.4" } }, + "node_modules/@acuminous/bitsyntax": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz", + "integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==", + "dependencies": { + "buffer-more-ints": "~1.0.0", + "debug": "^4.3.4", + "safe-buffer": "~5.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@acuminous/bitsyntax/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -1548,6 +1567,106 @@ "node": ">=8" } }, + "node_modules/@mobicoop/configuration-module": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mobicoop/configuration-module/-/configuration-module-1.1.0.tgz", + "integrity": "sha512-4yzCrY8m40XOO3CZnWJC4kHk66sTQCwe5UjKCV/UpNkN9IGUKW+R84J/53aulmGTL95vec7g6tFIwlHJd9BCoA==", + "dependencies": { + "@liaoliaots/nestjs-redis": "^9.0.5", + "@mobicoop/message-broker-module": "^1.0.4", + "@nestjs/cqrs": "^9.0.4", + "@types/amqplib": "^0.10.1", + "amqplib": "^0.10.3", + "class-validator": "^0.14.0", + "ioredis": "^5.3.2" + }, + "peerDependencies": { + "@nestjs/common": "^9.4.2" + } + }, + "node_modules/@mobicoop/configuration-module/node_modules/amqplib": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", + "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", + "dependencies": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "url-parse": "~1.5.10" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mobicoop/configuration-module/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/@mobicoop/configuration-module/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/@mobicoop/configuration-module/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "node_modules/@mobicoop/message-broker-module": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@mobicoop/message-broker-module/-/message-broker-module-1.0.6.tgz", + "integrity": "sha512-aVkWErc5pHz1oPRVBzvK3CvKKcUSNDvW58fbFXHbOA+md+jnyP9sH9NHyIOtVzIv0f6DbJBn9SA3x4VnSrDaBg==", + "dependencies": { + "@golevelup/nestjs-rabbitmq": "^3.6.0", + "@types/amqplib": "^0.10.1", + "amqplib": "^0.10.3" + }, + "peerDependencies": { + "@nestjs/common": "^9.4.2" + } + }, + "node_modules/@mobicoop/message-broker-module/node_modules/amqplib": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz", + "integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==", + "dependencies": { + "@acuminous/bitsyntax": "^0.1.2", + "buffer-more-ints": "~1.0.0", + "readable-stream": "1.x >=1.1.9", + "url-parse": "~1.5.10" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mobicoop/message-broker-module/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/@mobicoop/message-broker-module/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/@mobicoop/message-broker-module/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, "node_modules/@nestjs/cli": { "version": "9.4.2", "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.4.2.tgz", @@ -1632,12 +1751,12 @@ } }, "node_modules/@nestjs/common": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.4.0.tgz", - "integrity": "sha512-RUcVAQsEF4WPrmzFXEOUfZnPwrLTe1UVlzXTlSyfqfqbdWDPKDGlIPVelBLfc5/+RRUQ0I5iE4+CQvpCmkqldw==", + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.4.2.tgz", + "integrity": "sha512-sea+qZnbD5x3YWZDVQT/wbVJ2NiABaM1tyZTLuW9hpkcM2KFA96xKtK3VaCxyz49zoXIgSOefsyK7HuUMCe27Q==", "dependencies": { "iterare": "1.2.1", - "tslib": "2.5.0", + "tslib": "2.5.2", "uid": "2.0.2" }, "funding": { @@ -1663,6 +1782,11 @@ } } }, + "node_modules/@nestjs/common/node_modules/tslib": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz", + "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==" + }, "node_modules/@nestjs/config": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.3.1.tgz", @@ -1717,9 +1841,9 @@ } }, "node_modules/@nestjs/cqrs": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@nestjs/cqrs/-/cqrs-9.0.3.tgz", - "integrity": "sha512-hmbrqf51BVdgmnnxErnLVXfPNTEqr4Hz8DyLa9dKLIW3BuOyI5RDwJ/9sKbJ47UDBhumC5nQlNK9qk27mhqHfw==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/cqrs/-/cqrs-9.0.4.tgz", + "integrity": "sha512-nWDF+xs4jqs6OjxFg/wVSd0NiIV9+EFCJrJNTo4VRWe78CcAaitbp56CBspUh4gKyfkci95i+EhHdEqRXKFptg==", "dependencies": { "uuid": "9.0.0" }, @@ -2162,6 +2286,14 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "node_modules/@types/amqplib": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.1.tgz", + "integrity": "sha512-j6ANKT79ncUDnAs/+9r9eDujxbeJoTjoVu33gHHcaPfmLQaMhvfbH2GqSe8KUM444epAp1Vl3peVOQfZk3UIqA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", diff --git a/package.json b/package.json index 36dee54..e83732c 100644 --- a/package.json +++ b/package.json @@ -37,10 +37,11 @@ "@automapper/classes": "^8.7.7", "@automapper/core": "^8.7.7", "@automapper/nestjs": "^8.7.7", - "@golevelup/nestjs-rabbitmq": "^3.6.0", "@grpc/grpc-js": "^1.8.14", "@grpc/proto-loader": "^0.7.6", "@liaoliaots/nestjs-redis": "^9.0.5", + "@mobicoop/configuration-module": "^1.1.0", + "@mobicoop/message-broker-module": "^1.0.5", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.3.1", "@nestjs/core": "^9.0.0", @@ -93,6 +94,7 @@ ".presenter.ts", ".profile.ts", ".exception.ts", + ".constants.ts", "main.ts" ], "rootDir": "src", @@ -111,6 +113,8 @@ ".presenter.ts", ".profile.ts", ".exception.ts", + ".constants.ts", + ".interfaces.ts", "main.ts" ], "coverageDirectory": "../coverage", diff --git a/src/app.constants.ts b/src/app.constants.ts new file mode 100644 index 0000000..ff96e07 --- /dev/null +++ b/src/app.constants.ts @@ -0,0 +1,2 @@ +export const MESSAGE_BROKER_PUBLISHER = Symbol(); +export const MESSAGE_PUBLISHER = Symbol(); diff --git a/src/app.module.ts b/src/app.module.ts index 53070e4..4b2ab5c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,16 +1,65 @@ import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { ConfigurationModule } from './modules/configuration/configuration.module'; +import { ConfigModule, ConfigService } from '@nestjs/config'; import { HealthModule } from './modules/health/health.module'; import { AdModule } from './modules/ad/ad.module'; import { AutomapperModule } from '@automapper/nestjs'; import { classes } from '@automapper/classes'; +import { + MessageBrokerModule, + MessageBrokerModuleOptions, +} from '@mobicoop/message-broker-module'; +import { + ConfigurationModule, + ConfigurationModuleOptions, +} from '@mobicoop/configuration-module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), AutomapperModule.forRoot({ strategyInitializer: classes() }), - ConfigurationModule, + MessageBrokerModule.forRootAsync( + { + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async ( + configService: ConfigService, + ): Promise => ({ + uri: configService.get('MESSAGE_BROKER_URI'), + exchange: configService.get('MESSAGE_BROKER_EXCHANGE'), + }), + }, + false, + ), + ConfigurationModule.forRootAsync( + { + imports: [ConfigModule], + inject: [ConfigService], + useFactory: async ( + configService: ConfigService, + ): Promise => ({ + domain: configService.get('SERVICE_CONFIGURATION_DOMAIN'), + messageBroker: { + uri: configService.get('MESSAGE_BROKER_URI'), + exchange: configService.get('MESSAGE_BROKER_EXCHANGE'), + }, + redis: { + host: configService.get('REDIS_HOST'), + password: configService.get('REDIS_PASSWORD'), + port: configService.get('REDIS_PORT'), + }, + setConfigurationBrokerRoutingKeys: [ + 'configuration.create', + 'configuration.update', + ], + deleteConfigurationRoutingKey: 'configuration.delete', + propagateConfigurationRoutingKey: 'configuration.propagate', + setConfigurationBrokerQueue: 'ad-configuration-create-update', + deleteConfigurationQueue: 'ad-configuration-delete', + propagateConfigurationQueue: 'ad-configuration-propagate', + }), + }, + true, + ), HealthModule, AdModule, ], diff --git a/src/interfaces/message-publisher.ts b/src/interfaces/message-publisher.ts new file mode 100644 index 0000000..29ad456 --- /dev/null +++ b/src/interfaces/message-publisher.ts @@ -0,0 +1,3 @@ +export interface IPublishMessage { + publish(routingKey: string, message: string): void; +} diff --git a/src/modules/ad/ad.constants.ts b/src/modules/ad/ad.constants.ts new file mode 100644 index 0000000..08f6845 --- /dev/null +++ b/src/modules/ad/ad.constants.ts @@ -0,0 +1 @@ +export const PARAMS_PROVIDER = Symbol(); diff --git a/src/modules/ad/ad.module.ts b/src/modules/ad/ad.module.ts index 6f0c928..a7e0218 100644 --- a/src/modules/ad/ad.module.ts +++ b/src/modules/ad/ad.module.ts @@ -2,45 +2,36 @@ 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'; import { CreateAdUseCase } from './domain/usecases/create-ad.usecase'; import { DefaultParamsProvider } from './adapters/secondaries/default-params.provider'; +import { PARAMS_PROVIDER } from './ad.constants'; +import { MESSAGE_BROKER_PUBLISHER, MESSAGE_PUBLISHER } from 'src/app.constants'; +import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; +import { MessagePublisher } from './adapters/secondaries/message-publisher'; @Module({ - 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], - }), - ], + imports: [DatabaseModule, CqrsModule], controllers: [AdController], providers: [ AdProfile, AdsRepository, - Messager, FindAdByUuidUseCase, CreateAdUseCase, { - provide: 'ParamsProvider', + provide: PARAMS_PROVIDER, useClass: DefaultParamsProvider, }, + { + provide: MESSAGE_BROKER_PUBLISHER, + useClass: MessageBrokerPublisher, + }, + { + provide: MESSAGE_PUBLISHER, + useClass: MessagePublisher, + }, ], }) export class AdModule {} diff --git a/src/modules/ad/adapters/secondaries/message-publisher.ts b/src/modules/ad/adapters/secondaries/message-publisher.ts new file mode 100644 index 0000000..98a963b --- /dev/null +++ b/src/modules/ad/adapters/secondaries/message-publisher.ts @@ -0,0 +1,16 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { MESSAGE_BROKER_PUBLISHER } from '../../../../app.constants'; +import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; +import { IPublishMessage } from 'src/interfaces/message-publisher'; + +@Injectable() +export class MessagePublisher implements IPublishMessage { + constructor( + @Inject(MESSAGE_BROKER_PUBLISHER) + private readonly messageBrokerPublisher: MessageBrokerPublisher, + ) {} + + publish = (routingKey: string, message: string): void => { + this.messageBrokerPublisher.publish(routingKey, message); + }; +} diff --git a/src/modules/ad/adapters/secondaries/messager.ts b/src/modules/ad/adapters/secondaries/messager.ts deleted file mode 100644 index 8e584cf..0000000 --- a/src/modules/ad/adapters/secondaries/messager.ts +++ /dev/null @@ -1,18 +0,0 @@ -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/interfaces/message-broker.ts b/src/modules/ad/domain/interfaces/message-broker.ts deleted file mode 100644 index 594aa43..0000000 --- a/src/modules/ad/domain/interfaces/message-broker.ts +++ /dev/null @@ -1,12 +0,0 @@ -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/create-ad.usecase.ts b/src/modules/ad/domain/usecases/create-ad.usecase.ts index 6485953..ac62877 100644 --- a/src/modules/ad/domain/usecases/create-ad.usecase.ts +++ b/src/modules/ad/domain/usecases/create-ad.usecase.ts @@ -2,7 +2,6 @@ import { Mapper } from '@automapper/core'; import { InjectMapper } from '@automapper/nestjs'; import { Inject } from '@nestjs/common'; import { CommandHandler } from '@nestjs/cqrs'; -import { Messager } from '../../adapters/secondaries/messager'; import { AdsRepository } from '../../adapters/secondaries/ads.repository'; import { CreateAdCommand } from '../../commands/create-ad.command'; import { CreateAdRequest } from '../dtos/create-ad.request'; @@ -10,6 +9,9 @@ import { IProvideParams } from '../interfaces/param-provider.interface'; import { DefaultParams } from '../types/default-params.type'; import { AdCreation } from '../dtos/ad.creation'; import { Ad } from '../entities/ad'; +import { PARAMS_PROVIDER } from '../../ad.constants'; +import { IPublishMessage } from '../../../../interfaces/message-publisher'; +import { MESSAGE_PUBLISHER } from '../../../../app.constants'; @CommandHandler(CreateAdCommand) export class CreateAdUseCase { @@ -17,9 +19,10 @@ export class CreateAdUseCase { private ad: AdCreation; constructor( private readonly repository: AdsRepository, - private readonly messager: Messager, + @Inject(MESSAGE_PUBLISHER) + private readonly messagePublisher: IPublishMessage, @InjectMapper() private readonly mapper: Mapper, - @Inject('ParamsProvider') + @Inject(PARAMS_PROVIDER) private readonly defaultParamsProvider: IProvideParams, ) { this.defaultParams = defaultParamsProvider.getParams(); @@ -38,8 +41,8 @@ export class CreateAdUseCase { try { const adCreated: Ad = await this.repository.create(this.ad); - this.messager.publish('ad.create', JSON.stringify(adCreated)); - this.messager.publish( + this.messagePublisher.publish('ad.create', JSON.stringify(adCreated)); + this.messagePublisher.publish( 'logging.ad.create.info', JSON.stringify(adCreated), ); @@ -49,7 +52,7 @@ export class CreateAdUseCase { if (error.message.includes('Already exists')) { key = 'logging.ad.create.warning'; } - this.messager.publish( + this.messagePublisher.publish( key, JSON.stringify({ command, 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 index 719cc83..273b619 100644 --- a/src/modules/ad/domain/usecases/find-ad-by-uuid.usecase.ts +++ b/src/modules/ad/domain/usecases/find-ad-by-uuid.usecase.ts @@ -1,15 +1,17 @@ -import { NotFoundException } from '@nestjs/common'; +import { Inject, 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'; +import { MESSAGE_PUBLISHER } from '../../../../app.constants'; +import { IPublishMessage } from '../../../../interfaces/message-publisher'; @QueryHandler(FindAdByUuidQuery) export class FindAdByUuidUseCase { constructor( private readonly repository: AdsRepository, - private readonly messager: Messager, + @Inject(MESSAGE_PUBLISHER) + private readonly messagePublisher: IPublishMessage, ) {} async execute(findAdByUuid: FindAdByUuidQuery): Promise { @@ -18,7 +20,7 @@ export class FindAdByUuidUseCase { if (!ad) throw new NotFoundException(); return ad; } catch (error) { - this.messager.publish( + this.messagePublisher.publish( 'logging.ad.read.warning', JSON.stringify({ query: findAdByUuid, diff --git a/src/modules/ad/tests/unit/adapters/secondaries/message-publisher.spec.ts b/src/modules/ad/tests/unit/adapters/secondaries/message-publisher.spec.ts new file mode 100644 index 0000000..d32a536 --- /dev/null +++ b/src/modules/ad/tests/unit/adapters/secondaries/message-publisher.spec.ts @@ -0,0 +1,36 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MessagePublisher } from '../../../../adapters/secondaries/message-publisher'; +import { MESSAGE_BROKER_PUBLISHER } from '../../../../../../app.constants'; + +const mockMessageBrokerPublisher = { + publish: jest.fn().mockImplementation(), +}; + +describe('Messager', () => { + let messagePublisher: MessagePublisher; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ + MessagePublisher, + { + provide: MESSAGE_BROKER_PUBLISHER, + useValue: mockMessageBrokerPublisher, + }, + ], + }).compile(); + + messagePublisher = module.get(MessagePublisher); + }); + + it('should be defined', () => { + expect(messagePublisher).toBeDefined(); + }); + + it('should publish a message', async () => { + jest.spyOn(mockMessageBrokerPublisher, 'publish'); + messagePublisher.publish('health.info', 'my-test'); + expect(mockMessageBrokerPublisher.publish).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/ad/tests/unit/adapters/secondaries/messager.spec.ts b/src/modules/ad/tests/unit/adapters/secondaries/messager.spec.ts deleted file mode 100644 index b487762..0000000 --- a/src/modules/ad/tests/unit/adapters/secondaries/messager.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; -import { ConfigService } from '@nestjs/config'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Messager } from '../../../../adapters/secondaries/messager'; - -const mockAmqpConnection = { - publish: jest.fn().mockImplementation(), -}; - -const mockConfigService = { - get: jest.fn().mockResolvedValue({ - RMQ_EXCHANGE: 'mobicoop', - }), -}; - -describe('Messager', () => { - let messager: Messager; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [], - providers: [ - Messager, - { - provide: AmqpConnection, - useValue: mockAmqpConnection, - }, - { - provide: ConfigService, - useValue: mockConfigService, - }, - ], - }).compile(); - - messager = module.get(Messager); - }); - - it('should be defined', () => { - expect(messager).toBeDefined(); - }); - - it('should publish a message', async () => { - jest.spyOn(mockAmqpConnection, 'publish'); - messager.publish('ad.create.info', 'my-test'); - expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/modules/ad/tests/unit/domain/create-ad.usecase.spec.ts b/src/modules/ad/tests/unit/domain/create-ad.usecase.spec.ts index 0e31ee6..a816e0a 100644 --- a/src/modules/ad/tests/unit/domain/create-ad.usecase.spec.ts +++ b/src/modules/ad/tests/unit/domain/create-ad.usecase.spec.ts @@ -1,7 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CreateAdUseCase } from '../../../domain/usecases/create-ad.usecase'; import { CreateAdRequest } from '../../../domain/dtos/create-ad.request'; -import { Messager } from '../../../adapters/secondaries/messager'; import { AdsRepository } from '../../../adapters/secondaries/ads.repository'; import { CreateAdCommand } from '../../../commands/create-ad.command'; import { AutomapperModule } from '@automapper/nestjs'; @@ -11,7 +10,9 @@ import { Ad } from '../../../domain/entities/ad'; import { AdProfile } from '../../../mappers/ad.profile'; import { AddressDTO } from '../../../domain/dtos/address.dto'; import { AdCreation } from '../../../domain/dtos/ad.creation'; -import { Address } from 'src/modules/ad/domain/entities/address'; +import { Address } from '../../../domain/entities/address'; +import { PARAMS_PROVIDER } from '../../../ad.constants'; +import { MESSAGE_PUBLISHER } from '../../../../../app.constants'; const mockAddress1: AddressDTO = { position: 0, @@ -80,7 +81,7 @@ const newAdRequest: CreateAdRequest = { addresses: [mockAddress1, mockAddress2], }; -const mockMessager = { +const mockMessagePublisher = { publish: jest.fn().mockImplementation(), }; const mockDefaultParamsProvider = { @@ -128,13 +129,13 @@ describe('CreateAdUseCase', () => { useValue: mockAdRepository, }, { - provide: Messager, - useValue: mockMessager, + provide: MESSAGE_PUBLISHER, + useValue: mockMessagePublisher, }, CreateAdUseCase, AdProfile, { - provide: 'ParamsProvider', + provide: PARAMS_PROVIDER, useValue: mockDefaultParamsProvider, }, ], diff --git a/src/modules/ad/tests/unit/domain/find-ad-by-uuid.usecase.spec.ts b/src/modules/ad/tests/unit/domain/find-ad-by-uuid.usecase.spec.ts index 3872d46..d15ab75 100644 --- a/src/modules/ad/tests/unit/domain/find-ad-by-uuid.usecase.spec.ts +++ b/src/modules/ad/tests/unit/domain/find-ad-by-uuid.usecase.spec.ts @@ -1,10 +1,10 @@ import { NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { Messager } from '../../../adapters/secondaries/messager'; import { FindAdByUuidQuery } from '../../../queries/find-ad-by-uuid.query'; import { AdsRepository } from '../../../adapters/secondaries/ads.repository'; import { FindAdByUuidUseCase } from '../../../domain/usecases/find-ad-by-uuid.usecase'; import { FindAdByUuidRequest } from '../../../domain/dtos/find-ad-by-uuid.request'; +import { MESSAGE_PUBLISHER } from '../../../../../app.constants'; const mockAd = { uuid: 'bb281075-1b98-4456-89d6-c643d3044a91', @@ -22,7 +22,7 @@ const mockAdRepository = { }), }; -const mockMessager = { +const mockMessagePublisher = { publish: jest.fn().mockImplementation(), }; @@ -37,8 +37,8 @@ describe('FindAdByUuidUseCase', () => { useValue: mockAdRepository, }, { - provide: Messager, - useValue: mockMessager, + provide: MESSAGE_PUBLISHER, + useValue: mockMessagePublisher, }, FindAdByUuidUseCase, ], diff --git a/src/modules/configuration/adapters/primaries/configuration-messager.controller.ts b/src/modules/configuration/adapters/primaries/configuration-messager.controller.ts deleted file mode 100644 index c9408ca..0000000 --- a/src/modules/configuration/adapters/primaries/configuration-messager.controller.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq'; -import { Controller } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { CommandBus } from '@nestjs/cqrs'; -import { DeleteConfigurationCommand } from '../../commands/delete-configuration.command'; -import { SetConfigurationCommand } from '../../commands/set-configuration.command'; -import { DeleteConfigurationRequest } from '../../domain/dtos/delete-configuration.request'; -import { SetConfigurationRequest } from '../../domain/dtos/set-configuration.request'; -import { Configuration } from '../../domain/entities/configuration'; - -@Controller() -export class ConfigurationMessagerController { - constructor( - private readonly _commandBus: CommandBus, - private readonly _configService: ConfigService, - ) {} - - @RabbitSubscribe({ - name: 'setConfiguration', - }) - public async setConfigurationHandler(message: string) { - const configuration: Configuration = JSON.parse(message); - if ( - configuration.domain == - this._configService.get('SERVICE_CONFIGURATION_DOMAIN') - ) { - const setConfigurationRequest: SetConfigurationRequest = - new SetConfigurationRequest(); - setConfigurationRequest.domain = configuration.domain; - setConfigurationRequest.key = configuration.key; - setConfigurationRequest.value = configuration.value; - await this._commandBus.execute( - new SetConfigurationCommand(setConfigurationRequest), - ); - } - } - - @RabbitSubscribe({ - name: 'deleteConfiguration', - }) - public async configurationDeletedHandler(message: string) { - const deletedConfiguration: Configuration = JSON.parse(message); - if ( - deletedConfiguration.domain == - this._configService.get('SERVICE_CONFIGURATION_DOMAIN') - ) { - const deleteConfigurationRequest = new DeleteConfigurationRequest(); - deleteConfigurationRequest.domain = deletedConfiguration.domain; - deleteConfigurationRequest.key = deletedConfiguration.key; - await this._commandBus.execute( - new DeleteConfigurationCommand(deleteConfigurationRequest), - ); - } - } - - @RabbitSubscribe({ - name: 'propagateConfiguration', - }) - public async propagateConfigurationsHandler(message: string) { - const configurations: Array = JSON.parse(message); - configurations.forEach(async (configuration) => { - if ( - configuration.domain == - this._configService.get('SERVICE_CONFIGURATION_DOMAIN') - ) { - const setConfigurationRequest: SetConfigurationRequest = - new SetConfigurationRequest(); - setConfigurationRequest.domain = configuration.domain; - setConfigurationRequest.key = configuration.key; - setConfigurationRequest.value = configuration.value; - await this._commandBus.execute( - new SetConfigurationCommand(setConfigurationRequest), - ); - } - }); - } -} diff --git a/src/modules/configuration/adapters/secondaries/redis-configuration.repository.ts b/src/modules/configuration/adapters/secondaries/redis-configuration.repository.ts deleted file mode 100644 index 2de14f0..0000000 --- a/src/modules/configuration/adapters/secondaries/redis-configuration.repository.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { InjectRedis } from '@liaoliaots/nestjs-redis'; -import { Injectable } from '@nestjs/common'; -import { Redis } from 'ioredis'; -import { IConfigurationRepository } from '../../domain/interfaces/configuration.repository'; - -@Injectable() -export class RedisConfigurationRepository extends IConfigurationRepository { - constructor(@InjectRedis() private readonly _redis: Redis) { - super(); - } - - async get(key: string): Promise { - return await this._redis.get(key); - } - - async set(key: string, value: string) { - await this._redis.set(key, value); - } - - async del(key: string) { - await this._redis.del(key); - } -} diff --git a/src/modules/configuration/commands/delete-configuration.command.ts b/src/modules/configuration/commands/delete-configuration.command.ts deleted file mode 100644 index 8a6753e..0000000 --- a/src/modules/configuration/commands/delete-configuration.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DeleteConfigurationRequest } from '../domain/dtos/delete-configuration.request'; - -export class DeleteConfigurationCommand { - readonly deleteConfigurationRequest: DeleteConfigurationRequest; - - constructor(deleteConfigurationRequest: DeleteConfigurationRequest) { - this.deleteConfigurationRequest = deleteConfigurationRequest; - } -} diff --git a/src/modules/configuration/commands/set-configuration.command.ts b/src/modules/configuration/commands/set-configuration.command.ts deleted file mode 100644 index 52f54ee..0000000 --- a/src/modules/configuration/commands/set-configuration.command.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { SetConfigurationRequest } from '../domain/dtos/set-configuration.request'; - -export class SetConfigurationCommand { - readonly setConfigurationRequest: SetConfigurationRequest; - - constructor(setConfigurationRequest: SetConfigurationRequest) { - this.setConfigurationRequest = setConfigurationRequest; - } -} diff --git a/src/modules/configuration/configuration.module.ts b/src/modules/configuration/configuration.module.ts deleted file mode 100644 index fab6fe0..0000000 --- a/src/modules/configuration/configuration.module.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { RabbitMQConfig, RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; -import { RedisModule, RedisModuleOptions } from '@liaoliaots/nestjs-redis'; -import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { CqrsModule } from '@nestjs/cqrs'; -import { ConfigurationMessagerController } from './adapters/primaries/configuration-messager.controller'; -import { RedisConfigurationRepository } from './adapters/secondaries/redis-configuration.repository'; -import { DeleteConfigurationUseCase } from './domain/usecases/delete-configuration.usecase'; -import { GetConfigurationUseCase } from './domain/usecases/get-configuration.usecase'; -import { SetConfigurationUseCase } from './domain/usecases/set-configuration.usecase'; - -@Module({ - imports: [ - CqrsModule, - RedisModule.forRootAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: async ( - configService: ConfigService, - ): Promise => ({ - config: { - host: configService.get('REDIS_HOST'), - port: configService.get('REDIS_PORT'), - password: configService.get('REDIS_PASSWORD'), - }, - }), - }), - RabbitMQModule.forRootAsync(RabbitMQModule, { - imports: [ConfigModule], - inject: [ConfigService], - useFactory: async ( - configService: ConfigService, - ): Promise => ({ - exchanges: [ - { - name: configService.get('RMQ_EXCHANGE'), - type: 'topic', - }, - ], - handlers: { - setConfiguration: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: ['configuration.create', 'configuration.update'], - }, - deleteConfiguration: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: 'configuration.delete', - }, - propagateConfiguration: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: 'configuration.propagate', - }, - }, - uri: configService.get('RMQ_URI'), - connectionInitOptions: { wait: false }, - enableControllerDiscovery: true, - }), - }), - ], - controllers: [ConfigurationMessagerController], - providers: [ - GetConfigurationUseCase, - SetConfigurationUseCase, - DeleteConfigurationUseCase, - RedisConfigurationRepository, - ], -}) -export class ConfigurationModule {} diff --git a/src/modules/configuration/domain/dtos/delete-configuration.request.ts b/src/modules/configuration/domain/dtos/delete-configuration.request.ts deleted file mode 100644 index 3430832..0000000 --- a/src/modules/configuration/domain/dtos/delete-configuration.request.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; - -export class DeleteConfigurationRequest { - @IsString() - @IsNotEmpty() - domain: string; - - @IsString() - @IsNotEmpty() - key: string; -} diff --git a/src/modules/configuration/domain/dtos/set-configuration.request.ts b/src/modules/configuration/domain/dtos/set-configuration.request.ts deleted file mode 100644 index 3ed3fff..0000000 --- a/src/modules/configuration/domain/dtos/set-configuration.request.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; - -export class SetConfigurationRequest { - @IsString() - @IsNotEmpty() - domain: string; - - @IsString() - @IsNotEmpty() - key: string; - - @IsString() - @IsNotEmpty() - value: string; -} diff --git a/src/modules/configuration/domain/entities/configuration.ts b/src/modules/configuration/domain/entities/configuration.ts deleted file mode 100644 index 2008403..0000000 --- a/src/modules/configuration/domain/entities/configuration.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { AutoMap } from '@automapper/classes'; - -export class Configuration { - @AutoMap() - domain: string; - - @AutoMap() - key: string; - - @AutoMap() - value: string; -} diff --git a/src/modules/configuration/domain/interfaces/configuration.repository.ts b/src/modules/configuration/domain/interfaces/configuration.repository.ts deleted file mode 100644 index 657e5fd..0000000 --- a/src/modules/configuration/domain/interfaces/configuration.repository.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export abstract class IConfigurationRepository { - abstract get(key: string): Promise; - abstract set(key: string, value: string): void; - abstract del(key: string): void; -} diff --git a/src/modules/configuration/domain/usecases/delete-configuration.usecase.ts b/src/modules/configuration/domain/usecases/delete-configuration.usecase.ts deleted file mode 100644 index 14ab3cb..0000000 --- a/src/modules/configuration/domain/usecases/delete-configuration.usecase.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CommandHandler } from '@nestjs/cqrs'; -import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository'; -import { DeleteConfigurationCommand } from '../../commands/delete-configuration.command'; - -@CommandHandler(DeleteConfigurationCommand) -export class DeleteConfigurationUseCase { - constructor(private _configurationRepository: RedisConfigurationRepository) {} - - async execute(deleteConfigurationCommand: DeleteConfigurationCommand) { - await this._configurationRepository.del( - deleteConfigurationCommand.deleteConfigurationRequest.domain + - ':' + - deleteConfigurationCommand.deleteConfigurationRequest.key, - ); - } -} diff --git a/src/modules/configuration/domain/usecases/get-configuration.usecase.ts b/src/modules/configuration/domain/usecases/get-configuration.usecase.ts deleted file mode 100644 index 38036ff..0000000 --- a/src/modules/configuration/domain/usecases/get-configuration.usecase.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { QueryHandler } from '@nestjs/cqrs'; -import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository'; -import { GetConfigurationQuery } from '../../queries/get-configuration.query'; - -@QueryHandler(GetConfigurationQuery) -export class GetConfigurationUseCase { - constructor(private _configurationRepository: RedisConfigurationRepository) {} - - async execute(getConfigurationQuery: GetConfigurationQuery): Promise { - return this._configurationRepository.get( - getConfigurationQuery.domain + ':' + getConfigurationQuery.key, - ); - } -} diff --git a/src/modules/configuration/domain/usecases/set-configuration.usecase.ts b/src/modules/configuration/domain/usecases/set-configuration.usecase.ts deleted file mode 100644 index 408340a..0000000 --- a/src/modules/configuration/domain/usecases/set-configuration.usecase.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CommandHandler } from '@nestjs/cqrs'; -import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository'; -import { SetConfigurationCommand } from '../../commands/set-configuration.command'; - -@CommandHandler(SetConfigurationCommand) -export class SetConfigurationUseCase { - constructor(private _configurationRepository: RedisConfigurationRepository) {} - - async execute(setConfigurationCommand: SetConfigurationCommand) { - await this._configurationRepository.set( - setConfigurationCommand.setConfigurationRequest.domain + - ':' + - setConfigurationCommand.setConfigurationRequest.key, - setConfigurationCommand.setConfigurationRequest.value, - ); - } -} diff --git a/src/modules/configuration/queries/get-configuration.query.ts b/src/modules/configuration/queries/get-configuration.query.ts deleted file mode 100644 index 62211e7..0000000 --- a/src/modules/configuration/queries/get-configuration.query.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class GetConfigurationQuery { - readonly domain: string; - readonly key: string; - - constructor(domain: string, key: string) { - this.domain = domain; - this.key = key; - } -} diff --git a/src/modules/configuration/tests/unit/delete-configuration.usecase.spec.ts b/src/modules/configuration/tests/unit/delete-configuration.usecase.spec.ts deleted file mode 100644 index a28ae3c..0000000 --- a/src/modules/configuration/tests/unit/delete-configuration.usecase.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository'; -import { DeleteConfigurationCommand } from '../../commands/delete-configuration.command'; -import { DeleteConfigurationRequest } from '../../domain/dtos/delete-configuration.request'; -import { DeleteConfigurationUseCase } from '../../domain/usecases/delete-configuration.usecase'; - -const mockRedisConfigurationRepository = { - del: jest.fn().mockResolvedValue(undefined), -}; - -describe('DeleteConfigurationUseCase', () => { - let deleteConfigurationUseCase: DeleteConfigurationUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: RedisConfigurationRepository, - useValue: mockRedisConfigurationRepository, - }, - - DeleteConfigurationUseCase, - ], - }).compile(); - - deleteConfigurationUseCase = module.get( - DeleteConfigurationUseCase, - ); - }); - - it('should be defined', () => { - expect(deleteConfigurationUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should delete a key', async () => { - jest.spyOn(mockRedisConfigurationRepository, 'del'); - const deleteConfigurationRequest: DeleteConfigurationRequest = { - domain: 'my-domain', - key: 'my-key', - }; - await deleteConfigurationUseCase.execute( - new DeleteConfigurationCommand(deleteConfigurationRequest), - ); - - expect(mockRedisConfigurationRepository.del).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/src/modules/configuration/tests/unit/get-configuration.usecase.spec.ts b/src/modules/configuration/tests/unit/get-configuration.usecase.spec.ts deleted file mode 100644 index a94fc70..0000000 --- a/src/modules/configuration/tests/unit/get-configuration.usecase.spec.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository'; -import { GetConfigurationUseCase } from '../../domain/usecases/get-configuration.usecase'; -import { GetConfigurationQuery } from '../../queries/get-configuration.query'; - -const mockRedisConfigurationRepository = { - get: jest.fn().mockResolvedValue('my-value'), -}; - -describe('GetConfigurationUseCase', () => { - let getConfigurationUseCase: GetConfigurationUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: RedisConfigurationRepository, - useValue: mockRedisConfigurationRepository, - }, - - GetConfigurationUseCase, - ], - }).compile(); - - getConfigurationUseCase = module.get( - GetConfigurationUseCase, - ); - }); - - it('should be defined', () => { - expect(getConfigurationUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should get a value for a key', async () => { - const value: string = await getConfigurationUseCase.execute( - new GetConfigurationQuery('my-domain', 'my-key'), - ); - - expect(value).toBe('my-value'); - }); - }); -}); diff --git a/src/modules/configuration/tests/unit/redis-configuration.repository.spec.ts b/src/modules/configuration/tests/unit/redis-configuration.repository.spec.ts deleted file mode 100644 index 8efa436..0000000 --- a/src/modules/configuration/tests/unit/redis-configuration.repository.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository'; -import { getRedisToken } from '@liaoliaots/nestjs-redis'; - -const mockRedis = { - get: jest.fn().mockResolvedValue('myValue'), - set: jest.fn().mockImplementation(), - del: jest.fn().mockImplementation(), -}; - -describe('RedisConfigurationRepository', () => { - let redisConfigurationRepository: RedisConfigurationRepository; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: getRedisToken('default'), - useValue: mockRedis, - }, - RedisConfigurationRepository, - ], - }).compile(); - - redisConfigurationRepository = module.get( - RedisConfigurationRepository, - ); - }); - - it('should be defined', () => { - expect(redisConfigurationRepository).toBeDefined(); - }); - - describe('interact', () => { - it('should get a value', async () => { - expect(await redisConfigurationRepository.get('myKey')).toBe('myValue'); - }); - it('should set a value', async () => { - expect( - await redisConfigurationRepository.set('myKey', 'myValue'), - ).toBeUndefined(); - }); - it('should delete a value', async () => { - expect(await redisConfigurationRepository.del('myKey')).toBeUndefined(); - }); - }); -}); diff --git a/src/modules/configuration/tests/unit/set-configuration.usecase.spec.ts b/src/modules/configuration/tests/unit/set-configuration.usecase.spec.ts deleted file mode 100644 index f6e25d1..0000000 --- a/src/modules/configuration/tests/unit/set-configuration.usecase.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository'; -import { SetConfigurationCommand } from '../../commands/set-configuration.command'; -import { SetConfigurationRequest } from '../../domain/dtos/set-configuration.request'; -import { SetConfigurationUseCase } from '../../domain/usecases/set-configuration.usecase'; - -const mockRedisConfigurationRepository = { - set: jest.fn().mockResolvedValue(undefined), -}; - -describe('SetConfigurationUseCase', () => { - let setConfigurationUseCase: SetConfigurationUseCase; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - { - provide: RedisConfigurationRepository, - useValue: mockRedisConfigurationRepository, - }, - - SetConfigurationUseCase, - ], - }).compile(); - - setConfigurationUseCase = module.get( - SetConfigurationUseCase, - ); - }); - - it('should be defined', () => { - expect(setConfigurationUseCase).toBeDefined(); - }); - - describe('execute', () => { - it('should set a value for a key', async () => { - jest.spyOn(mockRedisConfigurationRepository, 'set'); - const setConfigurationRequest: SetConfigurationRequest = { - domain: 'my-domain', - key: 'my-key', - value: 'my-value', - }; - await setConfigurationUseCase.execute( - new SetConfigurationCommand(setConfigurationRequest), - ); - - expect(mockRedisConfigurationRepository.set).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/src/modules/health/adapters/primaries/health-server.controller.ts b/src/modules/health/adapters/primaries/health-server.controller.ts index b58c761..3cdc70d 100644 --- a/src/modules/health/adapters/primaries/health-server.controller.ts +++ b/src/modules/health/adapters/primaries/health-server.controller.ts @@ -1,6 +1,6 @@ import { Controller } from '@nestjs/common'; import { GrpcMethod } from '@nestjs/microservices'; -import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase'; +import { RepositoriesHealthIndicatorUseCase } from '../../domain/usecases/repositories.health-indicator.usecase'; enum ServingStatus { UNKNOWN = 0, @@ -19,7 +19,7 @@ interface HealthCheckResponse { @Controller() export class HealthServerController { constructor( - private readonly _prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase, + private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase, ) {} @GrpcMethod('Health', 'Check') @@ -29,12 +29,12 @@ export class HealthServerController { // eslint-disable-next-line @typescript-eslint/no-unused-vars metadata: any, ): Promise { - const healthCheck = await this._prismaHealthIndicatorUseCase.isHealthy( - 'prisma', + const healthCheck = await this.repositoriesHealthIndicatorUseCase.isHealthy( + 'repositories', ); return { status: - healthCheck['prisma'].status == 'up' + healthCheck['repositories'].status == 'up' ? ServingStatus.SERVING : ServingStatus.NOT_SERVING, }; diff --git a/src/modules/health/adapters/primaries/health.controller.ts b/src/modules/health/adapters/primaries/health.controller.ts index ee45f63..ba6ad9f 100644 --- a/src/modules/health/adapters/primaries/health.controller.ts +++ b/src/modules/health/adapters/primaries/health.controller.ts @@ -1,30 +1,33 @@ -import { Controller, Get } from '@nestjs/common'; +import { Controller, Get, Inject } from '@nestjs/common'; import { HealthCheckService, HealthCheck, HealthCheckResult, } from '@nestjs/terminus'; -import { Messager } from '../secondaries/messager'; -import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase'; +import { MESSAGE_PUBLISHER } from 'src/app.constants'; +import { IPublishMessage } from 'src/interfaces/message-publisher'; +import { RepositoriesHealthIndicatorUseCase } from '../../domain/usecases/repositories.health-indicator.usecase'; @Controller('health') export class HealthController { constructor( - private readonly _prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase, - private _healthCheckService: HealthCheckService, - private _messager: Messager, + private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase, + private healthCheckService: HealthCheckService, + @Inject(MESSAGE_PUBLISHER) + private readonly messagePublisher: IPublishMessage, ) {} @Get() @HealthCheck() async check() { try { - return await this._healthCheckService.check([ - async () => this._prismaHealthIndicatorUseCase.isHealthy('prisma'), + return await this.healthCheckService.check([ + async () => + this.repositoriesHealthIndicatorUseCase.isHealthy('repositories'), ]); } catch (error) { const healthCheckResult: HealthCheckResult = error.response; - this._messager.publish( + this.messagePublisher.publish( 'logging.user.health.crit', JSON.stringify(healthCheckResult.error), ); diff --git a/src/modules/health/adapters/secondaries/message-broker.ts b/src/modules/health/adapters/secondaries/message-broker.ts deleted file mode 100644 index 594aa43..0000000 --- a/src/modules/health/adapters/secondaries/message-broker.ts +++ /dev/null @@ -1,12 +0,0 @@ -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/health/adapters/secondaries/message-publisher.ts b/src/modules/health/adapters/secondaries/message-publisher.ts new file mode 100644 index 0000000..98a963b --- /dev/null +++ b/src/modules/health/adapters/secondaries/message-publisher.ts @@ -0,0 +1,16 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { MESSAGE_BROKER_PUBLISHER } from '../../../../app.constants'; +import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; +import { IPublishMessage } from 'src/interfaces/message-publisher'; + +@Injectable() +export class MessagePublisher implements IPublishMessage { + constructor( + @Inject(MESSAGE_BROKER_PUBLISHER) + private readonly messageBrokerPublisher: MessageBrokerPublisher, + ) {} + + publish = (routingKey: string, message: string): void => { + this.messageBrokerPublisher.publish(routingKey, message); + }; +} diff --git a/src/modules/health/adapters/secondaries/messager.ts b/src/modules/health/adapters/secondaries/messager.ts deleted file mode 100644 index 0725261..0000000 --- a/src/modules/health/adapters/secondaries/messager.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { IMessageBroker } from './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/health/domain/interfaces/check-repository.interface.ts b/src/modules/health/domain/interfaces/check-repository.interface.ts new file mode 100644 index 0000000..68c3178 --- /dev/null +++ b/src/modules/health/domain/interfaces/check-repository.interface.ts @@ -0,0 +1,3 @@ +export interface ICheckRepository { + healthCheck(): Promise; +} diff --git a/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts b/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts deleted file mode 100644 index 85a1868..0000000 --- a/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { - HealthCheckError, - HealthIndicator, - HealthIndicatorResult, -} from '@nestjs/terminus'; -import { AdsRepository } from '../../../ad/adapters/secondaries/ads.repository'; - -@Injectable() -export class PrismaHealthIndicatorUseCase extends HealthIndicator { - constructor(private readonly _repository: AdsRepository) { - super(); - } - - async isHealthy(key: string): Promise { - try { - await this._repository.healthCheck(); - return this.getStatus(key, true); - } catch (e) { - throw new HealthCheckError('Prisma', { - prisma: e.message, - }); - } - } -} diff --git a/src/modules/health/domain/usecases/repositories.health-indicator.usecase.ts b/src/modules/health/domain/usecases/repositories.health-indicator.usecase.ts new file mode 100644 index 0000000..f4db828 --- /dev/null +++ b/src/modules/health/domain/usecases/repositories.health-indicator.usecase.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { + HealthCheckError, + HealthIndicator, + HealthIndicatorResult, +} from '@nestjs/terminus'; +import { ICheckRepository } from '../interfaces/check-repository.interface'; +import { AdsRepository } from '../../../ad/adapters/secondaries/ads.repository'; + +@Injectable() +export class RepositoriesHealthIndicatorUseCase extends HealthIndicator { + private checkRepositories: ICheckRepository[]; + constructor(private readonly adsRepository: AdsRepository) { + super(); + this.checkRepositories = [adsRepository]; + } + isHealthy = async (key: string): Promise => { + try { + await Promise.all( + this.checkRepositories.map( + async (checkRepository: ICheckRepository) => { + await checkRepository.healthCheck(); + }, + ), + ); + return this.getStatus(key, true); + } catch (e: any) { + throw new HealthCheckError('Repository', { + repository: e.message, + }); + } + }; +} diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts index 5b4ef40..de1bc71 100644 --- a/src/modules/health/health.module.ts +++ b/src/modules/health/health.module.ts @@ -1,34 +1,28 @@ import { Module } from '@nestjs/common'; import { HealthServerController } from './adapters/primaries/health-server.controller'; -import { PrismaHealthIndicatorUseCase } from './domain/usecases/prisma.health-indicator.usecase'; import { AdsRepository } from '../ad/adapters/secondaries/ads.repository'; import { DatabaseModule } from '../database/database.module'; import { HealthController } from './adapters/primaries/health.controller'; import { TerminusModule } from '@nestjs/terminus'; -import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { Messager } from './adapters/secondaries/messager'; +import { MESSAGE_BROKER_PUBLISHER, MESSAGE_PUBLISHER } from 'src/app.constants'; +import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; +import { MessagePublisher } from './adapters/secondaries/message-publisher'; +import { RepositoriesHealthIndicatorUseCase } from './domain/usecases/repositories.health-indicator.usecase'; @Module({ - imports: [ - TerminusModule, - 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], - }), - DatabaseModule, - ], + imports: [TerminusModule, DatabaseModule], controllers: [HealthServerController, HealthController], - providers: [PrismaHealthIndicatorUseCase, AdsRepository, Messager], + providers: [ + RepositoriesHealthIndicatorUseCase, + AdsRepository, + { + provide: MESSAGE_BROKER_PUBLISHER, + useClass: MessageBrokerPublisher, + }, + { + provide: MESSAGE_PUBLISHER, + useClass: MessagePublisher, + }, + ], }) export class HealthModule {} diff --git a/src/modules/health/tests/unit/message-publisher.spec.ts b/src/modules/health/tests/unit/message-publisher.spec.ts new file mode 100644 index 0000000..eec02ea --- /dev/null +++ b/src/modules/health/tests/unit/message-publisher.spec.ts @@ -0,0 +1,36 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MessagePublisher } from '../../adapters/secondaries/message-publisher'; +import { MESSAGE_BROKER_PUBLISHER } from '../../../../app.constants'; + +const mockMessageBrokerPublisher = { + publish: jest.fn().mockImplementation(), +}; + +describe('Messager', () => { + let messagePublisher: MessagePublisher; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + imports: [], + providers: [ + MessagePublisher, + { + provide: MESSAGE_BROKER_PUBLISHER, + useValue: mockMessageBrokerPublisher, + }, + ], + }).compile(); + + messagePublisher = module.get(MessagePublisher); + }); + + it('should be defined', () => { + expect(messagePublisher).toBeDefined(); + }); + + it('should publish a message', async () => { + jest.spyOn(mockMessageBrokerPublisher, 'publish'); + messagePublisher.publish('health.info', 'my-test'); + expect(mockMessageBrokerPublisher.publish).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/health/tests/unit/messager.spec.ts b/src/modules/health/tests/unit/messager.spec.ts deleted file mode 100644 index 0331332..0000000 --- a/src/modules/health/tests/unit/messager.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; -import { ConfigService } from '@nestjs/config'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Messager } from '../../adapters/secondaries/messager'; - -const mockAmqpConnection = { - publish: jest.fn().mockImplementation(), -}; - -const mockConfigService = { - get: jest.fn().mockResolvedValue({ - RMQ_EXCHANGE: 'mobicoop', - }), -}; - -describe('Messager', () => { - let messager: Messager; - - beforeAll(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [], - providers: [ - Messager, - { - provide: AmqpConnection, - useValue: mockAmqpConnection, - }, - { - provide: ConfigService, - useValue: mockConfigService, - }, - ], - }).compile(); - - messager = module.get(Messager); - }); - - it('should be defined', () => { - expect(messager).toBeDefined(); - }); - - it('should publish a message', async () => { - jest.spyOn(mockAmqpConnection, 'publish'); - messager.publish('test.create.info', 'my-test'); - expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1); - }); -}); diff --git a/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts b/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts similarity index 54% rename from src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts rename to src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts index 887f635..0353505 100644 --- a/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts +++ b/src/modules/health/tests/unit/repositories.health-indicator.usecase.spec.ts @@ -1,8 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase'; import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus'; +import { RepositoriesHealthIndicatorUseCase } from '../../domain/usecases/repositories.health-indicator.usecase'; import { AdsRepository } from '../../../ad/adapters/secondaries/ads.repository'; -import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; const mockAdsRepository = { healthCheck: jest @@ -11,47 +10,45 @@ const mockAdsRepository = { return Promise.resolve(true); }) .mockImplementation(() => { - throw new PrismaClientKnownRequestError('Service unavailable', { - code: 'code', - clientVersion: 'version', - }); + throw new Error('an error occured in the repository'); }), }; -describe('PrismaHealthIndicatorUseCase', () => { - let prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase; +describe('RepositoriesHealthIndicatorUseCase', () => { + let repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ + RepositoriesHealthIndicatorUseCase, { provide: AdsRepository, useValue: mockAdsRepository, }, - PrismaHealthIndicatorUseCase, ], }).compile(); - prismaHealthIndicatorUseCase = module.get( - PrismaHealthIndicatorUseCase, - ); + repositoriesHealthIndicatorUseCase = + module.get( + RepositoriesHealthIndicatorUseCase, + ); }); it('should be defined', () => { - expect(prismaHealthIndicatorUseCase).toBeDefined(); + expect(repositoriesHealthIndicatorUseCase).toBeDefined(); }); describe('execute', () => { it('should check health successfully', async () => { const healthIndicatorResult: HealthIndicatorResult = - await prismaHealthIndicatorUseCase.isHealthy('prisma'); + await repositoriesHealthIndicatorUseCase.isHealthy('repositories'); - expect(healthIndicatorResult['prisma'].status).toBe('up'); + expect(healthIndicatorResult['repositories'].status).toBe('up'); }); it('should throw an error if database is unavailable', async () => { await expect( - prismaHealthIndicatorUseCase.isHealthy('prisma'), + repositoriesHealthIndicatorUseCase.isHealthy('repositories'), ).rejects.toBeInstanceOf(HealthCheckError); }); });