Merge branch 'healthUpgrade' into 'main'
upgrade health, use configuration and broker packages See merge request v3/service/ad!7
This commit is contained in:
commit
cd84567107
|
@ -7,9 +7,9 @@ HEALTH_SERVICE_PORT=6006
|
||||||
# PRISMA
|
# PRISMA
|
||||||
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=ad"
|
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=ad"
|
||||||
|
|
||||||
# RABBIT MQ
|
# MESSAGE BROKER
|
||||||
RMQ_URI=amqp://v3-broker:5672
|
MESSAGE_BROKER_URI=amqp://v3-broker:5672
|
||||||
RMQ_EXCHANGE=mobicoop
|
MESSAGE_BROKER_EXCHANGE=mobicoop
|
||||||
|
|
||||||
# REDIS
|
# REDIS
|
||||||
REDIS_HOST=v3-redis
|
REDIS_HOST=v3-redis
|
||||||
|
|
115
README.md
115
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)
|
- **Create** : create an ad (note that uuid is optional, a uuid will be automatically attributed if it is not provided)
|
||||||
|
|
||||||
|
Punctual driver ad :
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"userUuid": "113e0000-0000-4000-a000-000000000000",
|
"userUuid": "80c9bb02-0931-4a1d-bea6-22d358992245",
|
||||||
"driver": true,
|
"driver": true,
|
||||||
"passenger": false,
|
|
||||||
"frequency": "PUNCTUAL",
|
|
||||||
"departure": "2023-01-15",
|
|
||||||
"toDate": "2023-08-01",
|
|
||||||
|
|
||||||
"marginDurations": {
|
|
||||||
"mon": 800
|
|
||||||
},
|
|
||||||
"seatsPassenger": 0,
|
|
||||||
"seatsDriver": 3,
|
"seatsDriver": 3,
|
||||||
|
"frequency": "PUNCTUAL",
|
||||||
|
"departure": "2023-01-15 09:00",
|
||||||
"addresses": [
|
"addresses": [
|
||||||
{
|
{
|
||||||
|
"position": 0,
|
||||||
"lon": 48.68944505415954,
|
"lon": 48.68944505415954,
|
||||||
"lat": 6.176510296462267,
|
"lat": 6.176510296462267,
|
||||||
"houseNumber": "5",
|
"houseNumber": "5",
|
||||||
|
@ -83,6 +79,7 @@ The app exposes the following [gRPC](https://grpc.io/) services :
|
||||||
"country": "France"
|
"country": "France"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"position": 1,
|
||||||
"lon": 48.8566,
|
"lon": 48.8566,
|
||||||
"lat": 2.3522,
|
"lat": 2.3522,
|
||||||
"locality": "Paris",
|
"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
|
## Messages
|
||||||
|
|
||||||
As mentionned earlier, RabbitMQ messages are sent after these events :
|
As mentionned earlier, RabbitMQ messages are sent after these events :
|
||||||
|
|
|
@ -12,10 +12,11 @@
|
||||||
"@automapper/classes": "^8.7.7",
|
"@automapper/classes": "^8.7.7",
|
||||||
"@automapper/core": "^8.7.7",
|
"@automapper/core": "^8.7.7",
|
||||||
"@automapper/nestjs": "^8.7.7",
|
"@automapper/nestjs": "^8.7.7",
|
||||||
"@golevelup/nestjs-rabbitmq": "^3.6.0",
|
|
||||||
"@grpc/grpc-js": "^1.8.14",
|
"@grpc/grpc-js": "^1.8.14",
|
||||||
"@grpc/proto-loader": "^0.7.6",
|
"@grpc/proto-loader": "^0.7.6",
|
||||||
"@liaoliaots/nestjs-redis": "^9.0.5",
|
"@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/common": "^9.0.0",
|
||||||
"@nestjs/config": "^2.3.1",
|
"@nestjs/config": "^2.3.1",
|
||||||
"@nestjs/core": "^9.0.0",
|
"@nestjs/core": "^9.0.0",
|
||||||
|
@ -56,6 +57,24 @@
|
||||||
"typescript": "^4.7.4"
|
"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": {
|
"node_modules/@ampproject/remapping": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
|
||||||
|
@ -1548,6 +1567,106 @@
|
||||||
"node": ">=8"
|
"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": {
|
"node_modules/@nestjs/cli": {
|
||||||
"version": "9.4.2",
|
"version": "9.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.4.2.tgz",
|
||||||
|
@ -1632,12 +1751,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nestjs/common": {
|
"node_modules/@nestjs/common": {
|
||||||
"version": "9.4.0",
|
"version": "9.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.4.2.tgz",
|
||||||
"integrity": "sha512-RUcVAQsEF4WPrmzFXEOUfZnPwrLTe1UVlzXTlSyfqfqbdWDPKDGlIPVelBLfc5/+RRUQ0I5iE4+CQvpCmkqldw==",
|
"integrity": "sha512-sea+qZnbD5x3YWZDVQT/wbVJ2NiABaM1tyZTLuW9hpkcM2KFA96xKtK3VaCxyz49zoXIgSOefsyK7HuUMCe27Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"iterare": "1.2.1",
|
"iterare": "1.2.1",
|
||||||
"tslib": "2.5.0",
|
"tslib": "2.5.2",
|
||||||
"uid": "2.0.2"
|
"uid": "2.0.2"
|
||||||
},
|
},
|
||||||
"funding": {
|
"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": {
|
"node_modules/@nestjs/config": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/config/-/config-2.3.1.tgz",
|
||||||
|
@ -1717,9 +1841,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nestjs/cqrs": {
|
"node_modules/@nestjs/cqrs": {
|
||||||
"version": "9.0.3",
|
"version": "9.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/cqrs/-/cqrs-9.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/cqrs/-/cqrs-9.0.4.tgz",
|
||||||
"integrity": "sha512-hmbrqf51BVdgmnnxErnLVXfPNTEqr4Hz8DyLa9dKLIW3BuOyI5RDwJ/9sKbJ47UDBhumC5nQlNK9qk27mhqHfw==",
|
"integrity": "sha512-nWDF+xs4jqs6OjxFg/wVSd0NiIV9+EFCJrJNTo4VRWe78CcAaitbp56CBspUh4gKyfkci95i+EhHdEqRXKFptg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"uuid": "9.0.0"
|
"uuid": "9.0.0"
|
||||||
},
|
},
|
||||||
|
@ -2162,6 +2286,14 @@
|
||||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||||
"dev": true
|
"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": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.0",
|
"version": "7.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz",
|
||||||
|
|
|
@ -37,10 +37,11 @@
|
||||||
"@automapper/classes": "^8.7.7",
|
"@automapper/classes": "^8.7.7",
|
||||||
"@automapper/core": "^8.7.7",
|
"@automapper/core": "^8.7.7",
|
||||||
"@automapper/nestjs": "^8.7.7",
|
"@automapper/nestjs": "^8.7.7",
|
||||||
"@golevelup/nestjs-rabbitmq": "^3.6.0",
|
|
||||||
"@grpc/grpc-js": "^1.8.14",
|
"@grpc/grpc-js": "^1.8.14",
|
||||||
"@grpc/proto-loader": "^0.7.6",
|
"@grpc/proto-loader": "^0.7.6",
|
||||||
"@liaoliaots/nestjs-redis": "^9.0.5",
|
"@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/common": "^9.0.0",
|
||||||
"@nestjs/config": "^2.3.1",
|
"@nestjs/config": "^2.3.1",
|
||||||
"@nestjs/core": "^9.0.0",
|
"@nestjs/core": "^9.0.0",
|
||||||
|
@ -93,6 +94,7 @@
|
||||||
".presenter.ts",
|
".presenter.ts",
|
||||||
".profile.ts",
|
".profile.ts",
|
||||||
".exception.ts",
|
".exception.ts",
|
||||||
|
".constants.ts",
|
||||||
"main.ts"
|
"main.ts"
|
||||||
],
|
],
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
|
@ -111,6 +113,8 @@
|
||||||
".presenter.ts",
|
".presenter.ts",
|
||||||
".profile.ts",
|
".profile.ts",
|
||||||
".exception.ts",
|
".exception.ts",
|
||||||
|
".constants.ts",
|
||||||
|
".interfaces.ts",
|
||||||
"main.ts"
|
"main.ts"
|
||||||
],
|
],
|
||||||
"coverageDirectory": "../coverage",
|
"coverageDirectory": "../coverage",
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const MESSAGE_BROKER_PUBLISHER = Symbol();
|
||||||
|
export const MESSAGE_PUBLISHER = Symbol();
|
|
@ -1,16 +1,65 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
import { ConfigurationModule } from './modules/configuration/configuration.module';
|
|
||||||
import { HealthModule } from './modules/health/health.module';
|
import { HealthModule } from './modules/health/health.module';
|
||||||
import { AdModule } from './modules/ad/ad.module';
|
import { AdModule } from './modules/ad/ad.module';
|
||||||
import { AutomapperModule } from '@automapper/nestjs';
|
import { AutomapperModule } from '@automapper/nestjs';
|
||||||
import { classes } from '@automapper/classes';
|
import { classes } from '@automapper/classes';
|
||||||
|
import {
|
||||||
|
MessageBrokerModule,
|
||||||
|
MessageBrokerModuleOptions,
|
||||||
|
} from '@mobicoop/message-broker-module';
|
||||||
|
import {
|
||||||
|
ConfigurationModule,
|
||||||
|
ConfigurationModuleOptions,
|
||||||
|
} from '@mobicoop/configuration-module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot({ isGlobal: true }),
|
ConfigModule.forRoot({ isGlobal: true }),
|
||||||
AutomapperModule.forRoot({ strategyInitializer: classes() }),
|
AutomapperModule.forRoot({ strategyInitializer: classes() }),
|
||||||
ConfigurationModule,
|
MessageBrokerModule.forRootAsync(
|
||||||
|
{
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: async (
|
||||||
|
configService: ConfigService,
|
||||||
|
): Promise<MessageBrokerModuleOptions> => ({
|
||||||
|
uri: configService.get<string>('MESSAGE_BROKER_URI'),
|
||||||
|
exchange: configService.get<string>('MESSAGE_BROKER_EXCHANGE'),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
ConfigurationModule.forRootAsync(
|
||||||
|
{
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: async (
|
||||||
|
configService: ConfigService,
|
||||||
|
): Promise<ConfigurationModuleOptions> => ({
|
||||||
|
domain: configService.get<string>('SERVICE_CONFIGURATION_DOMAIN'),
|
||||||
|
messageBroker: {
|
||||||
|
uri: configService.get<string>('MESSAGE_BROKER_URI'),
|
||||||
|
exchange: configService.get<string>('MESSAGE_BROKER_EXCHANGE'),
|
||||||
|
},
|
||||||
|
redis: {
|
||||||
|
host: configService.get<string>('REDIS_HOST'),
|
||||||
|
password: configService.get<string>('REDIS_PASSWORD'),
|
||||||
|
port: configService.get<number>('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,
|
HealthModule,
|
||||||
AdModule,
|
AdModule,
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface IPublishMessage {
|
||||||
|
publish(routingKey: string, message: string): void;
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export const PARAMS_PROVIDER = Symbol();
|
|
@ -2,45 +2,36 @@ import { Module } from '@nestjs/common';
|
||||||
import { AdController } from './adapters/primaries/ad.controller';
|
import { AdController } from './adapters/primaries/ad.controller';
|
||||||
import { DatabaseModule } from '../database/database.module';
|
import { DatabaseModule } from '../database/database.module';
|
||||||
import { CqrsModule } from '@nestjs/cqrs';
|
import { CqrsModule } from '@nestjs/cqrs';
|
||||||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
||||||
import { AdProfile } from './mappers/ad.profile';
|
import { AdProfile } from './mappers/ad.profile';
|
||||||
import { AdsRepository } from './adapters/secondaries/ads.repository';
|
import { AdsRepository } from './adapters/secondaries/ads.repository';
|
||||||
import { Messager } from './adapters/secondaries/messager';
|
|
||||||
import { FindAdByUuidUseCase } from './domain/usecases/find-ad-by-uuid.usecase';
|
import { FindAdByUuidUseCase } from './domain/usecases/find-ad-by-uuid.usecase';
|
||||||
import { CreateAdUseCase } from './domain/usecases/create-ad.usecase';
|
import { CreateAdUseCase } from './domain/usecases/create-ad.usecase';
|
||||||
import { DefaultParamsProvider } from './adapters/secondaries/default-params.provider';
|
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({
|
@Module({
|
||||||
imports: [
|
imports: [DatabaseModule, CqrsModule],
|
||||||
DatabaseModule,
|
|
||||||
CqrsModule,
|
|
||||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
|
||||||
imports: [ConfigModule],
|
|
||||||
useFactory: async (configService: ConfigService) => ({
|
|
||||||
exchanges: [
|
|
||||||
{
|
|
||||||
name: configService.get<string>('RMQ_EXCHANGE'),
|
|
||||||
type: 'topic',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
uri: configService.get<string>('RMQ_URI'),
|
|
||||||
connectionInitOptions: { wait: false },
|
|
||||||
}),
|
|
||||||
inject: [ConfigService],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
controllers: [AdController],
|
controllers: [AdController],
|
||||||
providers: [
|
providers: [
|
||||||
AdProfile,
|
AdProfile,
|
||||||
AdsRepository,
|
AdsRepository,
|
||||||
Messager,
|
|
||||||
FindAdByUuidUseCase,
|
FindAdByUuidUseCase,
|
||||||
CreateAdUseCase,
|
CreateAdUseCase,
|
||||||
{
|
{
|
||||||
provide: 'ParamsProvider',
|
provide: PARAMS_PROVIDER,
|
||||||
useClass: DefaultParamsProvider,
|
useClass: DefaultParamsProvider,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: MESSAGE_BROKER_PUBLISHER,
|
||||||
|
useClass: MessageBrokerPublisher,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: MESSAGE_PUBLISHER,
|
||||||
|
useClass: MessagePublisher,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AdModule {}
|
export class AdModule {}
|
||||||
|
|
|
@ -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);
|
||||||
|
};
|
||||||
|
}
|
|
@ -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<string>('RMQ_EXCHANGE'));
|
|
||||||
}
|
|
||||||
|
|
||||||
publish(routingKey: string, message: string): void {
|
|
||||||
this.amqpConnection.publish(this.exchange, routingKey, message);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -2,7 +2,6 @@ import { Mapper } from '@automapper/core';
|
||||||
import { InjectMapper } from '@automapper/nestjs';
|
import { InjectMapper } from '@automapper/nestjs';
|
||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import { CommandHandler } from '@nestjs/cqrs';
|
import { CommandHandler } from '@nestjs/cqrs';
|
||||||
import { Messager } from '../../adapters/secondaries/messager';
|
|
||||||
import { AdsRepository } from '../../adapters/secondaries/ads.repository';
|
import { AdsRepository } from '../../adapters/secondaries/ads.repository';
|
||||||
import { CreateAdCommand } from '../../commands/create-ad.command';
|
import { CreateAdCommand } from '../../commands/create-ad.command';
|
||||||
import { CreateAdRequest } from '../dtos/create-ad.request';
|
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 { DefaultParams } from '../types/default-params.type';
|
||||||
import { AdCreation } from '../dtos/ad.creation';
|
import { AdCreation } from '../dtos/ad.creation';
|
||||||
import { Ad } from '../entities/ad';
|
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)
|
@CommandHandler(CreateAdCommand)
|
||||||
export class CreateAdUseCase {
|
export class CreateAdUseCase {
|
||||||
|
@ -17,9 +19,10 @@ export class CreateAdUseCase {
|
||||||
private ad: AdCreation;
|
private ad: AdCreation;
|
||||||
constructor(
|
constructor(
|
||||||
private readonly repository: AdsRepository,
|
private readonly repository: AdsRepository,
|
||||||
private readonly messager: Messager,
|
@Inject(MESSAGE_PUBLISHER)
|
||||||
|
private readonly messagePublisher: IPublishMessage,
|
||||||
@InjectMapper() private readonly mapper: Mapper,
|
@InjectMapper() private readonly mapper: Mapper,
|
||||||
@Inject('ParamsProvider')
|
@Inject(PARAMS_PROVIDER)
|
||||||
private readonly defaultParamsProvider: IProvideParams,
|
private readonly defaultParamsProvider: IProvideParams,
|
||||||
) {
|
) {
|
||||||
this.defaultParams = defaultParamsProvider.getParams();
|
this.defaultParams = defaultParamsProvider.getParams();
|
||||||
|
@ -38,8 +41,8 @@ export class CreateAdUseCase {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const adCreated: Ad = await this.repository.create(this.ad);
|
const adCreated: Ad = await this.repository.create(this.ad);
|
||||||
this.messager.publish('ad.create', JSON.stringify(adCreated));
|
this.messagePublisher.publish('ad.create', JSON.stringify(adCreated));
|
||||||
this.messager.publish(
|
this.messagePublisher.publish(
|
||||||
'logging.ad.create.info',
|
'logging.ad.create.info',
|
||||||
JSON.stringify(adCreated),
|
JSON.stringify(adCreated),
|
||||||
);
|
);
|
||||||
|
@ -49,7 +52,7 @@ export class CreateAdUseCase {
|
||||||
if (error.message.includes('Already exists')) {
|
if (error.message.includes('Already exists')) {
|
||||||
key = 'logging.ad.create.warning';
|
key = 'logging.ad.create.warning';
|
||||||
}
|
}
|
||||||
this.messager.publish(
|
this.messagePublisher.publish(
|
||||||
key,
|
key,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
command,
|
command,
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import { NotFoundException } from '@nestjs/common';
|
import { Inject, NotFoundException } from '@nestjs/common';
|
||||||
import { QueryHandler } from '@nestjs/cqrs';
|
import { QueryHandler } from '@nestjs/cqrs';
|
||||||
import { FindAdByUuidQuery } from '../../queries/find-ad-by-uuid.query';
|
import { FindAdByUuidQuery } from '../../queries/find-ad-by-uuid.query';
|
||||||
import { AdsRepository } from '../../adapters/secondaries/ads.repository';
|
import { AdsRepository } from '../../adapters/secondaries/ads.repository';
|
||||||
import { Messager } from '../../adapters/secondaries/messager';
|
|
||||||
import { Ad } from '../entities/ad';
|
import { Ad } from '../entities/ad';
|
||||||
|
import { MESSAGE_PUBLISHER } from '../../../../app.constants';
|
||||||
|
import { IPublishMessage } from '../../../../interfaces/message-publisher';
|
||||||
|
|
||||||
@QueryHandler(FindAdByUuidQuery)
|
@QueryHandler(FindAdByUuidQuery)
|
||||||
export class FindAdByUuidUseCase {
|
export class FindAdByUuidUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly repository: AdsRepository,
|
private readonly repository: AdsRepository,
|
||||||
private readonly messager: Messager,
|
@Inject(MESSAGE_PUBLISHER)
|
||||||
|
private readonly messagePublisher: IPublishMessage,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(findAdByUuid: FindAdByUuidQuery): Promise<Ad> {
|
async execute(findAdByUuid: FindAdByUuidQuery): Promise<Ad> {
|
||||||
|
@ -18,7 +20,7 @@ export class FindAdByUuidUseCase {
|
||||||
if (!ad) throw new NotFoundException();
|
if (!ad) throw new NotFoundException();
|
||||||
return ad;
|
return ad;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.messager.publish(
|
this.messagePublisher.publish(
|
||||||
'logging.ad.read.warning',
|
'logging.ad.read.warning',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
query: findAdByUuid,
|
query: findAdByUuid,
|
||||||
|
|
|
@ -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>(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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -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>(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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { CreateAdUseCase } from '../../../domain/usecases/create-ad.usecase';
|
import { CreateAdUseCase } from '../../../domain/usecases/create-ad.usecase';
|
||||||
import { CreateAdRequest } from '../../../domain/dtos/create-ad.request';
|
import { CreateAdRequest } from '../../../domain/dtos/create-ad.request';
|
||||||
import { Messager } from '../../../adapters/secondaries/messager';
|
|
||||||
import { AdsRepository } from '../../../adapters/secondaries/ads.repository';
|
import { AdsRepository } from '../../../adapters/secondaries/ads.repository';
|
||||||
import { CreateAdCommand } from '../../../commands/create-ad.command';
|
import { CreateAdCommand } from '../../../commands/create-ad.command';
|
||||||
import { AutomapperModule } from '@automapper/nestjs';
|
import { AutomapperModule } from '@automapper/nestjs';
|
||||||
|
@ -11,7 +10,9 @@ import { Ad } from '../../../domain/entities/ad';
|
||||||
import { AdProfile } from '../../../mappers/ad.profile';
|
import { AdProfile } from '../../../mappers/ad.profile';
|
||||||
import { AddressDTO } from '../../../domain/dtos/address.dto';
|
import { AddressDTO } from '../../../domain/dtos/address.dto';
|
||||||
import { AdCreation } from '../../../domain/dtos/ad.creation';
|
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 = {
|
const mockAddress1: AddressDTO = {
|
||||||
position: 0,
|
position: 0,
|
||||||
|
@ -80,7 +81,7 @@ const newAdRequest: CreateAdRequest = {
|
||||||
addresses: [mockAddress1, mockAddress2],
|
addresses: [mockAddress1, mockAddress2],
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockMessager = {
|
const mockMessagePublisher = {
|
||||||
publish: jest.fn().mockImplementation(),
|
publish: jest.fn().mockImplementation(),
|
||||||
};
|
};
|
||||||
const mockDefaultParamsProvider = {
|
const mockDefaultParamsProvider = {
|
||||||
|
@ -128,13 +129,13 @@ describe('CreateAdUseCase', () => {
|
||||||
useValue: mockAdRepository,
|
useValue: mockAdRepository,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: Messager,
|
provide: MESSAGE_PUBLISHER,
|
||||||
useValue: mockMessager,
|
useValue: mockMessagePublisher,
|
||||||
},
|
},
|
||||||
CreateAdUseCase,
|
CreateAdUseCase,
|
||||||
AdProfile,
|
AdProfile,
|
||||||
{
|
{
|
||||||
provide: 'ParamsProvider',
|
provide: PARAMS_PROVIDER,
|
||||||
useValue: mockDefaultParamsProvider,
|
useValue: mockDefaultParamsProvider,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { NotFoundException } from '@nestjs/common';
|
import { NotFoundException } from '@nestjs/common';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { Messager } from '../../../adapters/secondaries/messager';
|
|
||||||
import { FindAdByUuidQuery } from '../../../queries/find-ad-by-uuid.query';
|
import { FindAdByUuidQuery } from '../../../queries/find-ad-by-uuid.query';
|
||||||
import { AdsRepository } from '../../../adapters/secondaries/ads.repository';
|
import { AdsRepository } from '../../../adapters/secondaries/ads.repository';
|
||||||
import { FindAdByUuidUseCase } from '../../../domain/usecases/find-ad-by-uuid.usecase';
|
import { FindAdByUuidUseCase } from '../../../domain/usecases/find-ad-by-uuid.usecase';
|
||||||
import { FindAdByUuidRequest } from '../../../domain/dtos/find-ad-by-uuid.request';
|
import { FindAdByUuidRequest } from '../../../domain/dtos/find-ad-by-uuid.request';
|
||||||
|
import { MESSAGE_PUBLISHER } from '../../../../../app.constants';
|
||||||
|
|
||||||
const mockAd = {
|
const mockAd = {
|
||||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
@ -22,7 +22,7 @@ const mockAdRepository = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockMessager = {
|
const mockMessagePublisher = {
|
||||||
publish: jest.fn().mockImplementation(),
|
publish: jest.fn().mockImplementation(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ describe('FindAdByUuidUseCase', () => {
|
||||||
useValue: mockAdRepository,
|
useValue: mockAdRepository,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: Messager,
|
provide: MESSAGE_PUBLISHER,
|
||||||
useValue: mockMessager,
|
useValue: mockMessagePublisher,
|
||||||
},
|
},
|
||||||
FindAdByUuidUseCase,
|
FindAdByUuidUseCase,
|
||||||
],
|
],
|
||||||
|
|
|
@ -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<string>('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<string>('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<Configuration> = JSON.parse(message);
|
|
||||||
configurations.forEach(async (configuration) => {
|
|
||||||
if (
|
|
||||||
configuration.domain ==
|
|
||||||
this._configService.get<string>('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),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<string> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { DeleteConfigurationRequest } from '../domain/dtos/delete-configuration.request';
|
|
||||||
|
|
||||||
export class DeleteConfigurationCommand {
|
|
||||||
readonly deleteConfigurationRequest: DeleteConfigurationRequest;
|
|
||||||
|
|
||||||
constructor(deleteConfigurationRequest: DeleteConfigurationRequest) {
|
|
||||||
this.deleteConfigurationRequest = deleteConfigurationRequest;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { SetConfigurationRequest } from '../domain/dtos/set-configuration.request';
|
|
||||||
|
|
||||||
export class SetConfigurationCommand {
|
|
||||||
readonly setConfigurationRequest: SetConfigurationRequest;
|
|
||||||
|
|
||||||
constructor(setConfigurationRequest: SetConfigurationRequest) {
|
|
||||||
this.setConfigurationRequest = setConfigurationRequest;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<RedisModuleOptions> => ({
|
|
||||||
config: {
|
|
||||||
host: configService.get<string>('REDIS_HOST'),
|
|
||||||
port: configService.get<number>('REDIS_PORT'),
|
|
||||||
password: configService.get<string>('REDIS_PASSWORD'),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
|
||||||
imports: [ConfigModule],
|
|
||||||
inject: [ConfigService],
|
|
||||||
useFactory: async (
|
|
||||||
configService: ConfigService,
|
|
||||||
): Promise<RabbitMQConfig> => ({
|
|
||||||
exchanges: [
|
|
||||||
{
|
|
||||||
name: configService.get<string>('RMQ_EXCHANGE'),
|
|
||||||
type: 'topic',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
handlers: {
|
|
||||||
setConfiguration: {
|
|
||||||
exchange: configService.get<string>('RMQ_EXCHANGE'),
|
|
||||||
routingKey: ['configuration.create', 'configuration.update'],
|
|
||||||
},
|
|
||||||
deleteConfiguration: {
|
|
||||||
exchange: configService.get<string>('RMQ_EXCHANGE'),
|
|
||||||
routingKey: 'configuration.delete',
|
|
||||||
},
|
|
||||||
propagateConfiguration: {
|
|
||||||
exchange: configService.get<string>('RMQ_EXCHANGE'),
|
|
||||||
routingKey: 'configuration.propagate',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
uri: configService.get<string>('RMQ_URI'),
|
|
||||||
connectionInitOptions: { wait: false },
|
|
||||||
enableControllerDiscovery: true,
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
controllers: [ConfigurationMessagerController],
|
|
||||||
providers: [
|
|
||||||
GetConfigurationUseCase,
|
|
||||||
SetConfigurationUseCase,
|
|
||||||
DeleteConfigurationUseCase,
|
|
||||||
RedisConfigurationRepository,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class ConfigurationModule {}
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class DeleteConfigurationRequest {
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
domain: string;
|
|
||||||
|
|
||||||
@IsString()
|
|
||||||
@IsNotEmpty()
|
|
||||||
key: string;
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { AutoMap } from '@automapper/classes';
|
|
||||||
|
|
||||||
export class Configuration {
|
|
||||||
@AutoMap()
|
|
||||||
domain: string;
|
|
||||||
|
|
||||||
@AutoMap()
|
|
||||||
key: string;
|
|
||||||
|
|
||||||
@AutoMap()
|
|
||||||
value: string;
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export abstract class IConfigurationRepository {
|
|
||||||
abstract get(key: string): Promise<string>;
|
|
||||||
abstract set(key: string, value: string): void;
|
|
||||||
abstract del(key: string): void;
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<string> {
|
|
||||||
return this._configurationRepository.get(
|
|
||||||
getConfigurationQuery.domain + ':' + getConfigurationQuery.key,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
export class GetConfigurationQuery {
|
|
||||||
readonly domain: string;
|
|
||||||
readonly key: string;
|
|
||||||
|
|
||||||
constructor(domain: string, key: string) {
|
|
||||||
this.domain = domain;
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>(
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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>(
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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>(
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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>(
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Controller } from '@nestjs/common';
|
import { Controller } from '@nestjs/common';
|
||||||
import { GrpcMethod } from '@nestjs/microservices';
|
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 {
|
enum ServingStatus {
|
||||||
UNKNOWN = 0,
|
UNKNOWN = 0,
|
||||||
|
@ -19,7 +19,7 @@ interface HealthCheckResponse {
|
||||||
@Controller()
|
@Controller()
|
||||||
export class HealthServerController {
|
export class HealthServerController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase,
|
private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@GrpcMethod('Health', 'Check')
|
@GrpcMethod('Health', 'Check')
|
||||||
|
@ -29,12 +29,12 @@ export class HealthServerController {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
metadata: any,
|
metadata: any,
|
||||||
): Promise<HealthCheckResponse> {
|
): Promise<HealthCheckResponse> {
|
||||||
const healthCheck = await this._prismaHealthIndicatorUseCase.isHealthy(
|
const healthCheck = await this.repositoriesHealthIndicatorUseCase.isHealthy(
|
||||||
'prisma',
|
'repositories',
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
status:
|
status:
|
||||||
healthCheck['prisma'].status == 'up'
|
healthCheck['repositories'].status == 'up'
|
||||||
? ServingStatus.SERVING
|
? ServingStatus.SERVING
|
||||||
: ServingStatus.NOT_SERVING,
|
: ServingStatus.NOT_SERVING,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,30 +1,33 @@
|
||||||
import { Controller, Get } from '@nestjs/common';
|
import { Controller, Get, Inject } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
HealthCheckService,
|
HealthCheckService,
|
||||||
HealthCheck,
|
HealthCheck,
|
||||||
HealthCheckResult,
|
HealthCheckResult,
|
||||||
} from '@nestjs/terminus';
|
} from '@nestjs/terminus';
|
||||||
import { Messager } from '../secondaries/messager';
|
import { MESSAGE_PUBLISHER } from 'src/app.constants';
|
||||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
import { IPublishMessage } from 'src/interfaces/message-publisher';
|
||||||
|
import { RepositoriesHealthIndicatorUseCase } from '../../domain/usecases/repositories.health-indicator.usecase';
|
||||||
|
|
||||||
@Controller('health')
|
@Controller('health')
|
||||||
export class HealthController {
|
export class HealthController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase,
|
private readonly repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase,
|
||||||
private _healthCheckService: HealthCheckService,
|
private healthCheckService: HealthCheckService,
|
||||||
private _messager: Messager,
|
@Inject(MESSAGE_PUBLISHER)
|
||||||
|
private readonly messagePublisher: IPublishMessage,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@HealthCheck()
|
@HealthCheck()
|
||||||
async check() {
|
async check() {
|
||||||
try {
|
try {
|
||||||
return await this._healthCheckService.check([
|
return await this.healthCheckService.check([
|
||||||
async () => this._prismaHealthIndicatorUseCase.isHealthy('prisma'),
|
async () =>
|
||||||
|
this.repositoriesHealthIndicatorUseCase.isHealthy('repositories'),
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const healthCheckResult: HealthCheckResult = error.response;
|
const healthCheckResult: HealthCheckResult = error.response;
|
||||||
this._messager.publish(
|
this.messagePublisher.publish(
|
||||||
'logging.user.health.crit',
|
'logging.user.health.crit',
|
||||||
JSON.stringify(healthCheckResult.error),
|
JSON.stringify(healthCheckResult.error),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
};
|
||||||
|
}
|
|
@ -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<string>('RMQ_EXCHANGE'));
|
|
||||||
}
|
|
||||||
|
|
||||||
publish(routingKey: string, message: string): void {
|
|
||||||
this._amqpConnection.publish(this.exchange, routingKey, message);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface ICheckRepository {
|
||||||
|
healthCheck(): Promise<boolean>;
|
||||||
|
}
|
|
@ -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<HealthIndicatorResult> {
|
|
||||||
try {
|
|
||||||
await this._repository.healthCheck();
|
|
||||||
return this.getStatus(key, true);
|
|
||||||
} catch (e) {
|
|
||||||
throw new HealthCheckError('Prisma', {
|
|
||||||
prisma: e.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<HealthIndicatorResult> => {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,34 +1,28 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { HealthServerController } from './adapters/primaries/health-server.controller';
|
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 { AdsRepository } from '../ad/adapters/secondaries/ads.repository';
|
||||||
import { DatabaseModule } from '../database/database.module';
|
import { DatabaseModule } from '../database/database.module';
|
||||||
import { HealthController } from './adapters/primaries/health.controller';
|
import { HealthController } from './adapters/primaries/health.controller';
|
||||||
import { TerminusModule } from '@nestjs/terminus';
|
import { TerminusModule } from '@nestjs/terminus';
|
||||||
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
import { MESSAGE_BROKER_PUBLISHER, MESSAGE_PUBLISHER } from 'src/app.constants';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||||
import { Messager } from './adapters/secondaries/messager';
|
import { MessagePublisher } from './adapters/secondaries/message-publisher';
|
||||||
|
import { RepositoriesHealthIndicatorUseCase } from './domain/usecases/repositories.health-indicator.usecase';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [TerminusModule, DatabaseModule],
|
||||||
TerminusModule,
|
|
||||||
RabbitMQModule.forRootAsync(RabbitMQModule, {
|
|
||||||
imports: [ConfigModule],
|
|
||||||
useFactory: async (configService: ConfigService) => ({
|
|
||||||
exchanges: [
|
|
||||||
{
|
|
||||||
name: configService.get<string>('RMQ_EXCHANGE'),
|
|
||||||
type: 'topic',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
uri: configService.get<string>('RMQ_URI'),
|
|
||||||
connectionInitOptions: { wait: false },
|
|
||||||
}),
|
|
||||||
inject: [ConfigService],
|
|
||||||
}),
|
|
||||||
DatabaseModule,
|
|
||||||
],
|
|
||||||
controllers: [HealthServerController, HealthController],
|
controllers: [HealthServerController, HealthController],
|
||||||
providers: [PrismaHealthIndicatorUseCase, AdsRepository, Messager],
|
providers: [
|
||||||
|
RepositoriesHealthIndicatorUseCase,
|
||||||
|
AdsRepository,
|
||||||
|
{
|
||||||
|
provide: MESSAGE_BROKER_PUBLISHER,
|
||||||
|
useClass: MessageBrokerPublisher,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: MESSAGE_PUBLISHER,
|
||||||
|
useClass: MessagePublisher,
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class HealthModule {}
|
export class HealthModule {}
|
||||||
|
|
|
@ -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>(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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -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>(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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
|
||||||
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
||||||
|
import { RepositoriesHealthIndicatorUseCase } from '../../domain/usecases/repositories.health-indicator.usecase';
|
||||||
import { AdsRepository } from '../../../ad/adapters/secondaries/ads.repository';
|
import { AdsRepository } from '../../../ad/adapters/secondaries/ads.repository';
|
||||||
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
|
|
||||||
|
|
||||||
const mockAdsRepository = {
|
const mockAdsRepository = {
|
||||||
healthCheck: jest
|
healthCheck: jest
|
||||||
|
@ -11,47 +10,45 @@ const mockAdsRepository = {
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
})
|
})
|
||||||
.mockImplementation(() => {
|
.mockImplementation(() => {
|
||||||
throw new PrismaClientKnownRequestError('Service unavailable', {
|
throw new Error('an error occured in the repository');
|
||||||
code: 'code',
|
|
||||||
clientVersion: 'version',
|
|
||||||
});
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('PrismaHealthIndicatorUseCase', () => {
|
describe('RepositoriesHealthIndicatorUseCase', () => {
|
||||||
let prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase;
|
let repositoriesHealthIndicatorUseCase: RepositoriesHealthIndicatorUseCase;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
providers: [
|
providers: [
|
||||||
|
RepositoriesHealthIndicatorUseCase,
|
||||||
{
|
{
|
||||||
provide: AdsRepository,
|
provide: AdsRepository,
|
||||||
useValue: mockAdsRepository,
|
useValue: mockAdsRepository,
|
||||||
},
|
},
|
||||||
PrismaHealthIndicatorUseCase,
|
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
prismaHealthIndicatorUseCase = module.get<PrismaHealthIndicatorUseCase>(
|
repositoriesHealthIndicatorUseCase =
|
||||||
PrismaHealthIndicatorUseCase,
|
module.get<RepositoriesHealthIndicatorUseCase>(
|
||||||
);
|
RepositoriesHealthIndicatorUseCase,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be defined', () => {
|
it('should be defined', () => {
|
||||||
expect(prismaHealthIndicatorUseCase).toBeDefined();
|
expect(repositoriesHealthIndicatorUseCase).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('execute', () => {
|
describe('execute', () => {
|
||||||
it('should check health successfully', async () => {
|
it('should check health successfully', async () => {
|
||||||
const healthIndicatorResult: HealthIndicatorResult =
|
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 () => {
|
it('should throw an error if database is unavailable', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
prismaHealthIndicatorUseCase.isHealthy('prisma'),
|
repositoriesHealthIndicatorUseCase.isHealthy('repositories'),
|
||||||
).rejects.toBeInstanceOf(HealthCheckError);
|
).rejects.toBeInstanceOf(HealthCheckError);
|
||||||
});
|
});
|
||||||
});
|
});
|
Loading…
Reference in New Issue