add health check rest service
This commit is contained in:
parent
a4a236b10e
commit
a2841db6d5
|
@ -1,6 +1,7 @@
|
||||||
# SERVICE
|
# SERVICE
|
||||||
SERVICE_URL=0.0.0.0
|
SERVICE_URL=0.0.0.0
|
||||||
SERVICE_PORT=5003
|
SERVICE_PORT=5003
|
||||||
|
HEALTH_SERVICE_PORT=6003
|
||||||
|
|
||||||
# PRISMA
|
# PRISMA
|
||||||
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=configuration"
|
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=configuration"
|
||||||
|
|
|
@ -14,6 +14,7 @@ services:
|
||||||
command: npm run start:dev
|
command: npm run start:dev
|
||||||
ports:
|
ports:
|
||||||
- ${SERVICE_PORT:-5003}:${SERVICE_PORT:-5003}
|
- ${SERVICE_PORT:-5003}:${SERVICE_PORT:-5003}
|
||||||
|
- ${HEALTH_SERVICE_PORT:-6003}:${HEALTH_SERVICE_PORT:-6003}
|
||||||
networks:
|
networks:
|
||||||
v3-network:
|
v3-network:
|
||||||
aliases:
|
aliases:
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
"@nestjs/cqrs": "^9.0.1",
|
"@nestjs/cqrs": "^9.0.1",
|
||||||
"@nestjs/microservices": "^9.2.1",
|
"@nestjs/microservices": "^9.2.1",
|
||||||
"@nestjs/platform-express": "^9.0.0",
|
"@nestjs/platform-express": "^9.0.0",
|
||||||
|
"@nestjs/terminus": "^9.2.2",
|
||||||
"@prisma/client": "^4.9.0",
|
"@prisma/client": "^4.9.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
|
@ -1888,6 +1889,71 @@
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@nestjs/terminus": {
|
||||||
|
"version": "9.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nestjs/terminus/-/terminus-9.2.2.tgz",
|
||||||
|
"integrity": "sha512-AWUA8XLcgxWUjUFYHDqi42M7CZn2e+DEWxP+MqNAbMzz4ybB5jGcFK5Fy8qwaNBoWg6KMF1JiXOOygGXgk9ydg==",
|
||||||
|
"dependencies": {
|
||||||
|
"boxen": "5.1.2",
|
||||||
|
"check-disk-space": "3.3.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@grpc/grpc-js": "*",
|
||||||
|
"@grpc/proto-loader": "*",
|
||||||
|
"@mikro-orm/core": "*",
|
||||||
|
"@mikro-orm/nestjs": "*",
|
||||||
|
"@nestjs/axios": "*",
|
||||||
|
"@nestjs/common": "9.x",
|
||||||
|
"@nestjs/core": "9.x",
|
||||||
|
"@nestjs/microservices": "*",
|
||||||
|
"@nestjs/mongoose": "*",
|
||||||
|
"@nestjs/sequelize": "*",
|
||||||
|
"@nestjs/typeorm": "*",
|
||||||
|
"mongoose": "*",
|
||||||
|
"reflect-metadata": "0.1.x",
|
||||||
|
"rxjs": "7.x",
|
||||||
|
"sequelize": "*",
|
||||||
|
"typeorm": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@grpc/grpc-js": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@grpc/proto-loader": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@mikro-orm/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@mikro-orm/nestjs": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@nestjs/axios": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@nestjs/microservices": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@nestjs/mongoose": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@nestjs/sequelize": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@nestjs/typeorm": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"mongoose": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sequelize": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typeorm": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nestjs/testing": {
|
"node_modules/@nestjs/testing": {
|
||||||
"version": "9.2.1",
|
"version": "9.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.2.1.tgz",
|
||||||
|
@ -2862,6 +2928,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||||
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-align": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-colors": {
|
"node_modules/ansi-colors": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||||
|
@ -3214,6 +3288,53 @@
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/boxen": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-align": "^3.0.0",
|
||||||
|
"camelcase": "^6.2.0",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"cli-boxes": "^2.2.1",
|
||||||
|
"string-width": "^4.2.2",
|
||||||
|
"type-fest": "^0.20.2",
|
||||||
|
"widest-line": "^3.1.0",
|
||||||
|
"wrap-ansi": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/camelcase": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/boxen/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/brace-expansion": {
|
"node_modules/brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
@ -3411,6 +3532,14 @@
|
||||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
|
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/check-disk-space": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-iOrT8yCZjSnyNZ43476FE2rnssvgw5hnuwOM0hm8Nj1qa0v4ieUUEbCyxxsEliaoDUb/75yCOL71zkDiDBLbMQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
@ -3483,6 +3612,17 @@
|
||||||
"validator": "^13.7.0"
|
"validator": "^13.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cli-boxes": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cli-cursor": {
|
"node_modules/cli-cursor": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||||
|
@ -8440,7 +8580,6 @@
|
||||||
"version": "0.20.2",
|
"version": "0.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
|
@ -8721,6 +8860,17 @@
|
||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/widest-line": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/windows-release": {
|
"node_modules/windows-release": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",
|
||||||
|
@ -10254,6 +10404,15 @@
|
||||||
"pluralize": "8.0.0"
|
"pluralize": "8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@nestjs/terminus": {
|
||||||
|
"version": "9.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nestjs/terminus/-/terminus-9.2.2.tgz",
|
||||||
|
"integrity": "sha512-AWUA8XLcgxWUjUFYHDqi42M7CZn2e+DEWxP+MqNAbMzz4ybB5jGcFK5Fy8qwaNBoWg6KMF1JiXOOygGXgk9ydg==",
|
||||||
|
"requires": {
|
||||||
|
"boxen": "5.1.2",
|
||||||
|
"check-disk-space": "3.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@nestjs/testing": {
|
"@nestjs/testing": {
|
||||||
"version": "9.2.1",
|
"version": "9.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.2.1.tgz",
|
||||||
|
@ -11052,6 +11211,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ansi-align": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
|
||||||
|
"requires": {
|
||||||
|
"string-width": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ansi-colors": {
|
"ansi-colors": {
|
||||||
"version": "4.1.3",
|
"version": "4.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
|
||||||
|
@ -11330,6 +11497,37 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"boxen": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==",
|
||||||
|
"requires": {
|
||||||
|
"ansi-align": "^3.0.0",
|
||||||
|
"camelcase": "^6.2.0",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"cli-boxes": "^2.2.1",
|
||||||
|
"string-width": "^4.2.2",
|
||||||
|
"type-fest": "^0.20.2",
|
||||||
|
"widest-line": "^3.1.0",
|
||||||
|
"wrap-ansi": "^7.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"camelcase": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="
|
||||||
|
},
|
||||||
|
"chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"requires": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
|
@ -11460,6 +11658,11 @@
|
||||||
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
|
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"check-disk-space": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-iOrT8yCZjSnyNZ43476FE2rnssvgw5hnuwOM0hm8Nj1qa0v4ieUUEbCyxxsEliaoDUb/75yCOL71zkDiDBLbMQ=="
|
||||||
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
@ -11509,6 +11712,11 @@
|
||||||
"validator": "^13.7.0"
|
"validator": "^13.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cli-boxes": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw=="
|
||||||
|
},
|
||||||
"cli-cursor": {
|
"cli-cursor": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||||
|
@ -15182,8 +15390,7 @@
|
||||||
"type-fest": {
|
"type-fest": {
|
||||||
"version": "0.20.2",
|
"version": "0.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
|
||||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"type-is": {
|
"type-is": {
|
||||||
"version": "1.6.18",
|
"version": "1.6.18",
|
||||||
|
@ -15381,6 +15588,14 @@
|
||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"widest-line": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
|
||||||
|
"requires": {
|
||||||
|
"string-width": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"windows-release": {
|
"windows-release": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz",
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
"@nestjs/cqrs": "^9.0.1",
|
"@nestjs/cqrs": "^9.0.1",
|
||||||
"@nestjs/microservices": "^9.2.1",
|
"@nestjs/microservices": "^9.2.1",
|
||||||
"@nestjs/platform-express": "^9.0.0",
|
"@nestjs/platform-express": "^9.0.0",
|
||||||
|
"@nestjs/terminus": "^9.2.2",
|
||||||
"@prisma/client": "^4.9.0",
|
"@prisma/client": "^4.9.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
|
|
39
src/main.ts
39
src/main.ts
|
@ -4,24 +4,27 @@ import { join } from 'path';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
|
const app = await NestFactory.create(AppModule);
|
||||||
AppModule,
|
app.connectMicroservice<MicroserviceOptions>({
|
||||||
{
|
transport: Transport.TCP,
|
||||||
transport: Transport.GRPC,
|
});
|
||||||
options: {
|
app.connectMicroservice<MicroserviceOptions>({
|
||||||
package: ['configuration', 'health'],
|
transport: Transport.GRPC,
|
||||||
protoPath: [
|
options: {
|
||||||
join(
|
package: ['configuration', 'health'],
|
||||||
__dirname,
|
protoPath: [
|
||||||
'modules/configuration/adapters/primaries/configuration.proto',
|
join(
|
||||||
),
|
__dirname,
|
||||||
join(__dirname, 'modules/health/adapters/primaries/health.proto'),
|
'modules/configuration/adapters/primaries/configuration.proto',
|
||||||
],
|
),
|
||||||
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
|
join(__dirname, 'modules/health/adapters/primaries/health.proto'),
|
||||||
loader: { keepCase: true },
|
],
|
||||||
},
|
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
|
||||||
|
loader: { keepCase: true },
|
||||||
},
|
},
|
||||||
);
|
});
|
||||||
await app.listen();
|
|
||||||
|
await app.startAllMicroservices();
|
||||||
|
await app.listen(process.env.HEALTH_SERVICE_PORT);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
|
@ -180,4 +180,21 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async healthCheck(): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
await this._prisma.$queryRaw`SELECT 1`;
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof PrismaClientKnownRequestError) {
|
||||||
|
throw new DatabaseException(
|
||||||
|
PrismaClientKnownRequestError.name,
|
||||||
|
e.code,
|
||||||
|
e.message,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new DatabaseException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,23 @@ const mockPrismaService = {
|
||||||
|
|
||||||
return Promise.resolve([fakeEntities, fakeEntities.length]);
|
return Promise.resolve([fakeEntities, fakeEntities.length]);
|
||||||
}),
|
}),
|
||||||
|
$queryRaw: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
throw new PrismaClientKnownRequestError('unknown request', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.mockImplementation(() => {
|
||||||
|
throw new PrismaClientKnownRequestError('Database unavailable', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
}),
|
||||||
fake: {
|
fake: {
|
||||||
create: jest
|
create: jest
|
||||||
.fn()
|
.fn()
|
||||||
|
@ -422,4 +439,23 @@ describe('PrismaRepository', () => {
|
||||||
).rejects.toBeInstanceOf(DatabaseException);
|
).rejects.toBeInstanceOf(DatabaseException);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('healthCheck', () => {
|
||||||
|
it('should throw a DatabaseException for client error', async () => {
|
||||||
|
await expect(fakeRepository.healthCheck()).rejects.toBeInstanceOf(
|
||||||
|
DatabaseException,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a healthy result', async () => {
|
||||||
|
const res = await fakeRepository.healthCheck();
|
||||||
|
expect(res).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an exception if database is not available', async () => {
|
||||||
|
await expect(fakeRepository.healthCheck()).rejects.toBeInstanceOf(
|
||||||
|
DatabaseException,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { Controller } from '@nestjs/common';
|
||||||
|
import { GrpcMethod } from '@nestjs/microservices';
|
||||||
|
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
||||||
|
|
||||||
|
enum ServingStatus {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
SERVING = 1,
|
||||||
|
NOT_SERVING = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HealthCheckRequest {
|
||||||
|
service: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HealthCheckResponse {
|
||||||
|
status: ServingStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class HealthServerController {
|
||||||
|
constructor(
|
||||||
|
private readonly _prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@GrpcMethod('Health', 'Check')
|
||||||
|
async check(
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
data: HealthCheckRequest,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
metadata: any,
|
||||||
|
): Promise<HealthCheckResponse> {
|
||||||
|
const healthCheck = await this._prismaHealthIndicatorUseCase.isHealthy(
|
||||||
|
'prisma',
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
status:
|
||||||
|
healthCheck['prisma'].status == 'up'
|
||||||
|
? ServingStatus.SERVING
|
||||||
|
: ServingStatus.NOT_SERVING,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,34 @@
|
||||||
import { Controller } from '@nestjs/common';
|
import { Controller, Get } from '@nestjs/common';
|
||||||
import { GrpcMethod } from '@nestjs/microservices';
|
import {
|
||||||
|
HealthCheckService,
|
||||||
|
HealthCheck,
|
||||||
|
HealthCheckResult,
|
||||||
|
} from '@nestjs/terminus';
|
||||||
|
import { Messager } from '../secondaries/messager';
|
||||||
|
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
||||||
|
|
||||||
enum ServingStatus {
|
@Controller('health')
|
||||||
UNKNOWN = 0,
|
|
||||||
SERVING = 1,
|
|
||||||
NOT_SERVING = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HealthCheckRequest {
|
|
||||||
service: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface HealthCheckResponse {
|
|
||||||
status: ServingStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller()
|
|
||||||
export class HealthController {
|
export class HealthController {
|
||||||
@GrpcMethod('Health', 'Check')
|
constructor(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
private readonly _prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase,
|
||||||
check(data: HealthCheckRequest, metadata: any): HealthCheckResponse {
|
private _healthCheckService: HealthCheckService,
|
||||||
return {
|
private _messager: Messager,
|
||||||
status: ServingStatus.SERVING,
|
) {}
|
||||||
};
|
|
||||||
|
@Get()
|
||||||
|
@HealthCheck()
|
||||||
|
async check() {
|
||||||
|
try {
|
||||||
|
return await this._healthCheckService.check([
|
||||||
|
async () => this._prismaHealthIndicatorUseCase.isHealthy('prisma'),
|
||||||
|
]);
|
||||||
|
} catch (error) {
|
||||||
|
const healthCheckResult: HealthCheckResult = error.response;
|
||||||
|
this._messager.publish(
|
||||||
|
'logging.configuration.health.crit',
|
||||||
|
JSON.stringify(healthCheckResult.error),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export abstract class IMessageBroker {
|
||||||
|
exchange: string;
|
||||||
|
|
||||||
|
constructor(exchange: string) {
|
||||||
|
this.exchange = exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract publish(routingKey: string, message: string): void;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
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,25 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
HealthCheckError,
|
||||||
|
HealthIndicator,
|
||||||
|
HealthIndicatorResult,
|
||||||
|
} from '@nestjs/terminus';
|
||||||
|
import { ConfigurationRepository } from '../../../configuration/adapters/secondaries/configuration.repository';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PrismaHealthIndicatorUseCase extends HealthIndicator {
|
||||||
|
constructor(private readonly _repository: ConfigurationRepository) {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,34 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { HealthServerController } from './adapters/primaries/health-server.controller';
|
||||||
|
import { PrismaHealthIndicatorUseCase } from './domain/usecases/prisma.health-indicator.usecase';
|
||||||
|
import { ConfigurationRepository } from '../configuration/adapters/secondaries/configuration.repository';
|
||||||
|
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 { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
import { Messager } from './adapters/secondaries/messager';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [HealthController],
|
imports: [
|
||||||
|
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],
|
||||||
|
providers: [PrismaHealthIndicatorUseCase, ConfigurationRepository, Messager],
|
||||||
})
|
})
|
||||||
export class HealthModule {}
|
export class HealthModule {}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase';
|
||||||
|
import { ConfigurationRepository } from '../../../configuration/adapters/secondaries/configuration.repository';
|
||||||
|
import { PrismaClientKnownRequestError } from '@prisma/client/runtime';
|
||||||
|
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
|
||||||
|
|
||||||
|
const mockConfigurationRepository = {
|
||||||
|
healthCheck: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementationOnce(() => {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
})
|
||||||
|
.mockImplementation(() => {
|
||||||
|
throw new PrismaClientKnownRequestError('Service unavailable', {
|
||||||
|
code: 'code',
|
||||||
|
clientVersion: 'version',
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('PrismaHealthIndicatorUseCase', () => {
|
||||||
|
let prismaHealthIndicatorUseCase: PrismaHealthIndicatorUseCase;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: ConfigurationRepository,
|
||||||
|
useValue: mockConfigurationRepository,
|
||||||
|
},
|
||||||
|
PrismaHealthIndicatorUseCase,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
prismaHealthIndicatorUseCase = module.get<PrismaHealthIndicatorUseCase>(
|
||||||
|
PrismaHealthIndicatorUseCase,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(prismaHealthIndicatorUseCase).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('execute', () => {
|
||||||
|
it('should check health successfully', async () => {
|
||||||
|
const healthIndicatorResult: HealthIndicatorResult =
|
||||||
|
await prismaHealthIndicatorUseCase.isHealthy('prisma');
|
||||||
|
|
||||||
|
expect(healthIndicatorResult['prisma'].status).toBe('up');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if database is unavailable', async () => {
|
||||||
|
await expect(
|
||||||
|
prismaHealthIndicatorUseCase.isHealthy('prisma'),
|
||||||
|
).rejects.toBeInstanceOf(HealthCheckError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue