diff --git a/api/lib/infrastructure/repositories/jobs/certification-completed-job-repository.js b/api/lib/infrastructure/repositories/jobs/certification-completed-job-repository.js index 7cfaf173bff..c70ddd73c80 100644 --- a/api/lib/infrastructure/repositories/jobs/certification-completed-job-repository.js +++ b/api/lib/infrastructure/repositories/jobs/certification-completed-job-repository.js @@ -1,7 +1,6 @@ -import { JobPgBoss } from '../../../../src/shared/infrastructure/jobs/JobPgBoss.js'; -import { JobPriority } from '../../../../src/shared/infrastructure/jobs/JobPriority.js'; +import { JobPriority, JobRepository } from '../../../../src/shared/infrastructure/repositories/jobs/job-repository.js'; import { CertificationCompletedJob } from '../../../domain/events/CertificationCompleted.js'; -class CertificationCompletedJobRepository extends JobPgBoss { +class CertificationCompletedJobRepository extends JobRepository { constructor() { super({ name: CertificationCompletedJob.name, diff --git a/api/lib/infrastructure/repositories/jobs/user-anonymized-event-logging-job-repository.js b/api/lib/infrastructure/repositories/jobs/user-anonymized-event-logging-job-repository.js index eaba91d9f24..d76eb5623f3 100644 --- a/api/lib/infrastructure/repositories/jobs/user-anonymized-event-logging-job-repository.js +++ b/api/lib/infrastructure/repositories/jobs/user-anonymized-event-logging-job-repository.js @@ -1,7 +1,7 @@ import { UserAnonymizedEventLoggingJob } from '../../../../src/identity-access-management/domain/models/UserAnonymizedEventLoggingJob.js'; -import { JobPgBoss } from '../../../../src/shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../src/shared/infrastructure/repositories/jobs/job-repository.js'; -class UserAnonymizedEventLoggingJobRepository extends JobPgBoss { +class UserAnonymizedEventLoggingJobRepository extends JobRepository { constructor() { super({ name: UserAnonymizedEventLoggingJob.name, diff --git a/api/package-lock.json b/api/package-lock.json index 33b5fb0a1da..1634578eb88 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -37,6 +37,7 @@ "dotenv": "^16.0.1", "fast-levenshtein": "^3.0.0", "file-type": "^19.0.0", + "glob": "^11.0.0", "hapi-i18n": "^3.0.1", "hapi-sentry": "^4.0.0-0", "hapi-swagger": "^17.0.0", @@ -2184,7 +2185,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -2202,7 +2202,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2215,7 +2214,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -2562,7 +2560,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -4234,7 +4231,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -5438,7 +5434,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, "license": "MIT" }, "node_modules/ecc-jsbn": { @@ -5471,7 +5466,6 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/end-of-stream": { @@ -6941,7 +6935,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -7162,22 +7155,24 @@ } }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -7195,16 +7190,15 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -7577,6 +7571,43 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/html-validate/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/html-validate/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/html-validate/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -7584,6 +7615,46 @@ "dev": true, "license": "MIT" }, + "node_modules/html-validate/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/html-validate/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/html-validate/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/html-validate/node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -8005,14 +8076,16 @@ "license": "MIT" }, "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", + "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -9046,7 +9119,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -9962,7 +10034,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/pako": { @@ -10052,28 +10123,29 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz", + "integrity": "sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, "node_modules/path-to-regexp": { "version": "6.2.2", @@ -11650,7 +11722,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -12019,7 +12090,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -12038,7 +12108,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -12053,14 +12122,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12073,7 +12140,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -12102,7 +12168,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -12788,7 +12853,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -12807,7 +12871,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -12825,7 +12888,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -12841,7 +12903,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -12854,21 +12915,18 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -12883,7 +12941,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12896,7 +12953,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12909,7 +12965,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" diff --git a/api/package.json b/api/package.json index 150c6b3e4c1..bc315369b97 100644 --- a/api/package.json +++ b/api/package.json @@ -43,6 +43,7 @@ "dotenv": "^16.0.1", "fast-levenshtein": "^3.0.0", "file-type": "^19.0.0", + "glob": "^11.0.0", "hapi-i18n": "^3.0.1", "hapi-sentry": "^4.0.0-0", "hapi-swagger": "^17.0.0", diff --git a/api/src/certification/scoring/application/jobs/certification-completed-job-controller.js b/api/src/certification/scoring/application/jobs/certification-completed-job-controller.js index e0f04a08e72..3af25134fb6 100644 --- a/api/src/certification/scoring/application/jobs/certification-completed-job-controller.js +++ b/api/src/certification/scoring/application/jobs/certification-completed-job-controller.js @@ -1,5 +1,7 @@ +import { CertificationCompletedJob } from '../../../../../lib/domain/events/CertificationCompleted.js'; import { CertificationScoringCompleted } from '../../../../../lib/domain/events/CertificationScoringCompleted.js'; import * as events from '../../../../../lib/domain/events/index.js'; +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; import { V3_REPRODUCIBILITY_RATE } from '../../../../shared/domain/constants.js'; import { CertificationComputeError } from '../../../../shared/domain/errors.js'; import { AssessmentResult } from '../../../../shared/domain/models/index.js'; @@ -19,7 +21,11 @@ import * as certificationAssessmentHistoryRepository from '../../infrastructure/ import * as certificationChallengeForScoringRepository from '../../infrastructure/repositories/certification-challenge-for-scoring-repository.js'; import * as scoringConfigurationRepository from '../../infrastructure/repositories/scoring-configuration-repository.js'; -export class CertificationCompletedJobController { +export class CertificationCompletedJobController extends JobController { + constructor() { + super(CertificationCompletedJob.name); + } + async handle( certificationCompletedJob, dependencies = { diff --git a/api/src/certification/session-management/application/jobs/certification-rescoring-by-script-job-controller.js b/api/src/certification/session-management/application/jobs/certification-rescoring-by-script-job-controller.js index 820188f9f30..00e17ad8184 100644 --- a/api/src/certification/session-management/application/jobs/certification-rescoring-by-script-job-controller.js +++ b/api/src/certification/session-management/application/jobs/certification-rescoring-by-script-job-controller.js @@ -1,7 +1,13 @@ import CertificationRescoredByScript from '../../../../../lib/domain/events/CertificationRescoredByScript.js'; import { eventDispatcher } from '../../../../../lib/domain/events/index.js'; +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; +import { CertificationRescoringByScriptJob } from '../../domain/models/CertificationRescoringByScriptJob.js'; + +class CertificationRescoringByScriptJobController extends JobController { + constructor() { + super(CertificationRescoringByScriptJob.name); + } -class CertificationRescoringByScriptJobController { /** * @param {Object} event * @param {number} event.certificationCourseId diff --git a/api/src/certification/session-management/infrastructure/repositories/jobs/certification-rescoring-by-script-job-repository.js b/api/src/certification/session-management/infrastructure/repositories/jobs/certification-rescoring-by-script-job-repository.js index 7a57e2f0db4..7f779182d22 100644 --- a/api/src/certification/session-management/infrastructure/repositories/jobs/certification-rescoring-by-script-job-repository.js +++ b/api/src/certification/session-management/infrastructure/repositories/jobs/certification-rescoring-by-script-job-repository.js @@ -1,7 +1,7 @@ -import { JobPgBoss } from '../../../../../shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../../shared/infrastructure/repositories/jobs/job-repository.js'; import { CertificationRescoringByScriptJob } from '../../../domain/models/CertificationRescoringByScriptJob.js'; -class CertificationRescoringByScriptJobRepository extends JobPgBoss { +class CertificationRescoringByScriptJobRepository extends JobRepository { constructor() { super({ name: CertificationRescoringByScriptJob.name, diff --git a/api/src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging-job.controller.js b/api/src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging.job-controller.js similarity index 78% rename from api/src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging-job.controller.js rename to api/src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging.job-controller.js index c184e474400..6acbe9c8327 100644 --- a/api/src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging-job.controller.js +++ b/api/src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging.job-controller.js @@ -1,11 +1,12 @@ import { auditLoggerRepository } from '../../../../lib/infrastructure/repositories/audit-logger-repository.js'; +import { JobController } from '../../../shared/application/jobs/job-controller.js'; import { GarAnonymizedBatchEventsLoggingJob } from '../../domain/models/GarAnonymizedBatchEventsLoggingJob.js'; const AUDIT_LOGGER_ANONYMIZATION_GAR_ACTION = 'ANONYMIZATION_GAR'; -export class GarAnonymizedBatchEventsLoggingJobController { - get name() { - return GarAnonymizedBatchEventsLoggingJob.name; +export class GarAnonymizedBatchEventsLoggingJobController extends JobController { + constructor() { + super(GarAnonymizedBatchEventsLoggingJob.name); } async handle( diff --git a/api/src/identity-access-management/infrastructure/repositories/jobs/gar-anonymized-batch-events-logging-job-repository.js b/api/src/identity-access-management/infrastructure/repositories/jobs/gar-anonymized-batch-events-logging-job-repository.js index 88105bcf62a..3bd2bcb3011 100644 --- a/api/src/identity-access-management/infrastructure/repositories/jobs/gar-anonymized-batch-events-logging-job-repository.js +++ b/api/src/identity-access-management/infrastructure/repositories/jobs/gar-anonymized-batch-events-logging-job-repository.js @@ -1,7 +1,7 @@ -import { JobPgBoss } from '../../../../shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../shared/infrastructure/repositories/jobs/job-repository.js'; import { GarAnonymizedBatchEventsLoggingJob } from '../../../domain/models/GarAnonymizedBatchEventsLoggingJob.js'; -class GarAnonymizedBatchEventsLoggingJobRepository extends JobPgBoss { +class GarAnonymizedBatchEventsLoggingJobRepository extends JobRepository { constructor() { super({ name: GarAnonymizedBatchEventsLoggingJob.name, diff --git a/api/src/prescription/campaign-participation/application/jobs/participation-result-calculation-job-controller.js b/api/src/prescription/campaign-participation/application/jobs/participation-result-calculation-job-controller.js index fdcb146d6e3..38e0455f299 100644 --- a/api/src/prescription/campaign-participation/application/jobs/participation-result-calculation-job-controller.js +++ b/api/src/prescription/campaign-participation/application/jobs/participation-result-calculation-job-controller.js @@ -1,6 +1,12 @@ import { usecases } from '../../../../../lib/domain/usecases/index.js'; +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; +import { ParticipationResultCalculationJob } from '../../domain/models/ParticipationResultCalculationJob.js'; + +export class ParticipationResultCalculationJobController extends JobController { + constructor() { + super(ParticipationResultCalculationJob.name); + } -export class ParticipationResultCalculationJobController { async handle(scheduleParticipationResultCalculation) { const { campaignParticipationId } = scheduleParticipationResultCalculation; diff --git a/api/src/prescription/campaign-participation/application/jobs/pole-emploi-participation-completed-job-controller.js b/api/src/prescription/campaign-participation/application/jobs/pole-emploi-participation-completed-job-controller.js index 1599f40d3b1..da84477ff5b 100644 --- a/api/src/prescription/campaign-participation/application/jobs/pole-emploi-participation-completed-job-controller.js +++ b/api/src/prescription/campaign-participation/application/jobs/pole-emploi-participation-completed-job-controller.js @@ -10,10 +10,16 @@ import * as targetProfileRepository from '../../../../../lib/infrastructure/repo import { assessmentRepository } from '../../../../certification/session-management/infrastructure/repositories/index.js'; import * as authenticationMethodRepository from '../../../../identity-access-management/infrastructure/repositories/authentication-method.repository.js'; import * as userRepository from '../../../../identity-access-management/infrastructure/repositories/user.repository.js'; +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; import { PoleEmploiSending } from '../../../../shared/domain/models/index.js'; import * as organizationRepository from '../../../../shared/infrastructure/repositories/organization-repository.js'; +import { PoleEmploiParticipationCompletedJob } from '../../domain/models/PoleEmploiParticipationCompletedJob.js'; + +export class PoleEmploiParticipationCompletedJobController extends JobController { + constructor() { + super(PoleEmploiParticipationCompletedJob.name); + } -export class PoleEmploiParticipationCompletedJobController { async handle( campaignParticipationCompletedJob, dependencies = { diff --git a/api/src/prescription/campaign-participation/application/jobs/pole-emploi-participation-started-job-controller.js b/api/src/prescription/campaign-participation/application/jobs/pole-emploi-participation-started-job-controller.js index 5db5a83672d..772a4630777 100644 --- a/api/src/prescription/campaign-participation/application/jobs/pole-emploi-participation-started-job-controller.js +++ b/api/src/prescription/campaign-participation/application/jobs/pole-emploi-participation-started-job-controller.js @@ -8,11 +8,17 @@ import * as poleEmploiSendingRepository from '../../../../../lib/infrastructure/ import * as targetProfileRepository from '../../../../../lib/infrastructure/repositories/target-profile-repository.js'; import * as authenticationMethodRepository from '../../../../identity-access-management/infrastructure/repositories/authentication-method.repository.js'; import * as userRepository from '../../../../identity-access-management/infrastructure/repositories/user.repository.js'; +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; import { PoleEmploiSending } from '../../../../shared/domain/models/index.js'; import * as organizationRepository from '../../../../shared/infrastructure/repositories/organization-repository.js'; +import { PoleEmploiParticipationStartedJob } from '../../domain/models/PoleEmploiParticipationStartedJob.js'; import * as campaignParticipationRepository from '../../infrastructure/repositories/campaign-participation-repository.js'; -export class PoleEmploiParticipationStartedJobController { +export class PoleEmploiParticipationStartedJobController extends JobController { + constructor() { + super(PoleEmploiParticipationStartedJob.name); + } + async handle( data, dependencies = { diff --git a/api/src/prescription/campaign-participation/application/jobs/send-share-participation-results-to-pole-emploi-job-controller.js b/api/src/prescription/campaign-participation/application/jobs/send-share-participation-results-to-pole-emploi-job-controller.js index 89812f60bbc..c39a1825fd1 100644 --- a/api/src/prescription/campaign-participation/application/jobs/send-share-participation-results-to-pole-emploi-job-controller.js +++ b/api/src/prescription/campaign-participation/application/jobs/send-share-participation-results-to-pole-emploi-job-controller.js @@ -1,6 +1,12 @@ import { usecases } from '../../../../../lib/domain/usecases/index.js'; +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; +import { SendSharedParticipationResultsToPoleEmploiJob } from '../../domain/models/SendSharedParticipationResultsToPoleEmploiJob.js'; + +export class SendSharedParticipationResultsToPoleEmploiJobController extends JobController { + constructor() { + super(SendSharedParticipationResultsToPoleEmploiJob.name); + } -export class SendSharedParticipationResultsToPoleEmploiJobController { async handle(sendSharedParticipationResultsToPoleEmploiJob) { const { campaignParticipationId } = sendSharedParticipationResultsToPoleEmploiJob; diff --git a/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/participation-result-calculation-job-repository.js b/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/participation-result-calculation-job-repository.js index 2c597ab4b08..7b656557520 100644 --- a/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/participation-result-calculation-job-repository.js +++ b/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/participation-result-calculation-job-repository.js @@ -1,7 +1,7 @@ -import { JobPgBoss } from '../../../../../shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../../shared/infrastructure/repositories/jobs/job-repository.js'; import { ParticipationResultCalculationJob } from '../../../domain/models/ParticipationResultCalculationJob.js'; -class ParticipationResultCalculationJobRepository extends JobPgBoss { +class ParticipationResultCalculationJobRepository extends JobRepository { constructor() { super({ name: ParticipationResultCalculationJob.name, diff --git a/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/pole-emploi-participation-completed-job-repository.js b/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/pole-emploi-participation-completed-job-repository.js index fb5d427ecd9..e61129d1775 100644 --- a/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/pole-emploi-participation-completed-job-repository.js +++ b/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/pole-emploi-participation-completed-job-repository.js @@ -1,7 +1,7 @@ -import { JobPgBoss } from '../../../../../shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../../shared/infrastructure/repositories/jobs/job-repository.js'; import { PoleEmploiParticipationCompletedJob } from '../../../domain/models/PoleEmploiParticipationCompletedJob.js'; -class PoleEmploiParticipationCompletedJobRepository extends JobPgBoss { +class PoleEmploiParticipationCompletedJobRepository extends JobRepository { constructor() { super({ name: PoleEmploiParticipationCompletedJob.name, diff --git a/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/pole-emploi-participation-started-job-repository.js b/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/pole-emploi-participation-started-job-repository.js index 3358c820362..5db7e114e93 100644 --- a/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/pole-emploi-participation-started-job-repository.js +++ b/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/pole-emploi-participation-started-job-repository.js @@ -1,8 +1,8 @@ -import { JobPgBoss } from '../../../../../../src/shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../../../src/shared/infrastructure/repositories/jobs/job-repository.js'; import { DomainTransaction } from '../../../../../shared/domain/DomainTransaction.js'; import { PoleEmploiParticipationStartedJob } from '../../../domain/models/PoleEmploiParticipationStartedJob.js'; -class PoleEmploiParticipationStartedJobRepository extends JobPgBoss { +class PoleEmploiParticipationStartedJobRepository extends JobRepository { constructor() { super( { diff --git a/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/send-share-participation-results-to-pole-emploi-job-repository.js b/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/send-share-participation-results-to-pole-emploi-job-repository.js index 89b49bcaf44..bb7b7cb9635 100644 --- a/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/send-share-participation-results-to-pole-emploi-job-repository.js +++ b/api/src/prescription/campaign-participation/infrastructure/repositories/jobs/send-share-participation-results-to-pole-emploi-job-repository.js @@ -1,7 +1,7 @@ -import { JobPgBoss } from '../../../../../shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../../shared/infrastructure/repositories/jobs/job-repository.js'; import { SendSharedParticipationResultsToPoleEmploiJob } from '../../../domain/models/SendSharedParticipationResultsToPoleEmploiJob.js'; -class SendSharedParticipationResultsToPoleEmploiJobRepository extends JobPgBoss { +class SendSharedParticipationResultsToPoleEmploiJobRepository extends JobRepository { constructor() { super({ name: SendSharedParticipationResultsToPoleEmploiJob.name, diff --git a/api/src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js b/api/src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js index 774296d9228..f98ebd28fdf 100644 --- a/api/src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js +++ b/api/src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js @@ -1,16 +1,16 @@ import { usecases } from '../../../../../lib/domain/usecases/index.js'; +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; import { ComputeCertificabilityJob } from '../../domain/models/ComputeCertificabilityJob.js'; -class ComputeCertificabilityJobController { +class ComputeCertificabilityJobController extends JobController { + constructor() { + super(ComputeCertificabilityJob.name); + } async handle(data) { const { organizationLearnerId } = data; await usecases.computeOrganizationLearnerCertificability({ organizationLearnerId }); } - - get name() { - return ComputeCertificabilityJob.name; - } } export { ComputeCertificabilityJobController }; diff --git a/api/src/prescription/learner-management/application/jobs/import-organization-learners-job-controller.js b/api/src/prescription/learner-management/application/jobs/import-organization-learners-job-controller.js index 37f627109f5..e2806ecefc2 100644 --- a/api/src/prescription/learner-management/application/jobs/import-organization-learners-job-controller.js +++ b/api/src/prescription/learner-management/application/jobs/import-organization-learners-job-controller.js @@ -1,6 +1,17 @@ +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; +import { config } from '../../../../shared/config.js'; +import { ImportOrganizationLearnersJob } from '../../domain/models/ImportOrganizationLearnersJob.js'; import { usecases } from '../../domain/usecases/index.js'; -class ImportOrganizationLearnersJobController { +class ImportOrganizationLearnersJobController extends JobController { + constructor() { + super(ImportOrganizationLearnersJob.name); + } + + isJobEnabled() { + return config.pgBoss.importFileJobEnabled; + } + async handle(data) { const { organizationImportId } = data; diff --git a/api/src/prescription/learner-management/application/jobs/schedule-compute-organization-learners-certificability-job-controller.js b/api/src/prescription/learner-management/application/jobs/schedule-compute-organization-learners-certificability-job-controller.js index 2930b821a2f..ec13fa7b12b 100644 --- a/api/src/prescription/learner-management/application/jobs/schedule-compute-organization-learners-certificability-job-controller.js +++ b/api/src/prescription/learner-management/application/jobs/schedule-compute-organization-learners-certificability-job-controller.js @@ -3,12 +3,18 @@ import dayjs from 'dayjs'; import * as organizationLearnerRepository from '../../../../../lib/infrastructure/repositories/organization-learner-repository.js'; import { ComputeCertificabilityJob } from '../../../../prescription/learner-management/domain/models/ComputeCertificabilityJob.js'; +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; import { config } from '../../../../shared/config.js'; import { DomainTransaction } from '../../../../shared/domain/DomainTransaction.js'; import { logger } from '../../../../shared/infrastructure/utils/logger.js'; import { computeCertificabilityJobRepository } from '../../../learner-management/infrastructure/repositories/jobs/compute-certificability-job-repository.js'; +import { ScheduleComputeOrganizationLearnersCertificabilityJob } from '../../domain/models/ScheduleComputeOrganizationLearnersCertificabilityJob.js'; + +class ScheduleComputeOrganizationLearnersCertificabilityJobController extends JobController { + constructor() { + super(ScheduleComputeOrganizationLearnersCertificabilityJob.name); + } -class ScheduleComputeOrganizationLearnersCertificabilityJobController { async handle( event = {}, dependencies = { organizationLearnerRepository, computeCertificabilityJobRepository, config, logger }, diff --git a/api/src/prescription/learner-management/application/jobs/validate-organization-learners-import-file-job-controller.js b/api/src/prescription/learner-management/application/jobs/validate-organization-learners-import-file-job-controller.js index 3706246f03a..b675d751528 100644 --- a/api/src/prescription/learner-management/application/jobs/validate-organization-learners-import-file-job-controller.js +++ b/api/src/prescription/learner-management/application/jobs/validate-organization-learners-import-file-job-controller.js @@ -1,6 +1,17 @@ +import { JobController } from '../../../../shared/application/jobs/job-controller.js'; +import { config } from '../../../../shared/config.js'; +import { ValidateOrganizationImportFileJob } from '../../domain/models/ValidateOrganizationImportFileJob.js'; import { usecases } from '../../domain/usecases/index.js'; -class ValidateOrganizationLearnersImportFileJobController { +class ValidateOrganizationLearnersImportFileJobController extends JobController { + constructor() { + super(ValidateOrganizationImportFileJob.name); + } + + isJobEnabled() { + return config.pgBoss.validationFileJobEnabled; + } + async handle(event) { const { organizationImportId } = event; diff --git a/api/src/prescription/learner-management/infrastructure/repositories/jobs/compute-certificability-job-repository.js b/api/src/prescription/learner-management/infrastructure/repositories/jobs/compute-certificability-job-repository.js index b152903873c..da32492c6d2 100644 --- a/api/src/prescription/learner-management/infrastructure/repositories/jobs/compute-certificability-job-repository.js +++ b/api/src/prescription/learner-management/infrastructure/repositories/jobs/compute-certificability-job-repository.js @@ -1,7 +1,7 @@ -import { JobPgBoss } from '../../../../../shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../../shared/infrastructure/repositories/jobs/job-repository.js'; import { ComputeCertificabilityJob } from '../../../domain/models/ComputeCertificabilityJob.js'; -class ComputeCertificabilityJobRepository extends JobPgBoss { +class ComputeCertificabilityJobRepository extends JobRepository { constructor() { super({ name: ComputeCertificabilityJob.name, diff --git a/api/src/prescription/learner-management/infrastructure/repositories/jobs/import-organization-learners-job-repository.js b/api/src/prescription/learner-management/infrastructure/repositories/jobs/import-organization-learners-job-repository.js index e3eeb6c5fe2..819adcc1090 100644 --- a/api/src/prescription/learner-management/infrastructure/repositories/jobs/import-organization-learners-job-repository.js +++ b/api/src/prescription/learner-management/infrastructure/repositories/jobs/import-organization-learners-job-repository.js @@ -1,7 +1,7 @@ -import { JobPgBoss } from '../../../../../shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../../shared/infrastructure/repositories/jobs/job-repository.js'; import { ImportOrganizationLearnersJob } from '../../../domain/models/ImportOrganizationLearnersJob.js'; -class ImportOrganizationLearnersJobRepository extends JobPgBoss { +class ImportOrganizationLearnersJobRepository extends JobRepository { constructor() { super({ name: ImportOrganizationLearnersJob.name, diff --git a/api/src/prescription/learner-management/infrastructure/repositories/jobs/validate-organization-learners-import-file-job-repository.js.js b/api/src/prescription/learner-management/infrastructure/repositories/jobs/validate-organization-learners-import-file-job-repository.js.js index 97cbc005226..1d98691d2e6 100644 --- a/api/src/prescription/learner-management/infrastructure/repositories/jobs/validate-organization-learners-import-file-job-repository.js.js +++ b/api/src/prescription/learner-management/infrastructure/repositories/jobs/validate-organization-learners-import-file-job-repository.js.js @@ -1,7 +1,7 @@ -import { JobPgBoss } from '../../../../../shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../../../shared/infrastructure/repositories/jobs/job-repository.js'; import { ValidateOrganizationImportFileJob } from '../../../domain/models/ValidateOrganizationImportFileJob.js'; -class ValidateOrganizationImportFileJobRepository extends JobPgBoss { +class ValidateOrganizationImportFileJobRepository extends JobRepository { constructor() { super({ name: ValidateOrganizationImportFileJob.name, diff --git a/api/src/shared/application/jobs/audit-log/user-anonymized-event-logging-job-controller.js b/api/src/shared/application/jobs/audit-log/user-anonymized-event-logging-job-controller.js index db88bededa0..800eb998623 100644 --- a/api/src/shared/application/jobs/audit-log/user-anonymized-event-logging-job-controller.js +++ b/api/src/shared/application/jobs/audit-log/user-anonymized-event-logging-job-controller.js @@ -1,6 +1,12 @@ import { auditLoggerRepository } from '../../../../../lib/infrastructure/repositories/audit-logger-repository.js'; +import { UserAnonymizedEventLoggingJob } from '../../../../identity-access-management/domain/models/UserAnonymizedEventLoggingJob.js'; +import { JobController } from '../job-controller.js'; + +export class UserAnonymizedEventLoggingJobController extends JobController { + constructor() { + super(UserAnonymizedEventLoggingJob.name); + } -export class UserAnonymizedEventLoggingJobController { async handle(UserAnonymizedEventLoggingJob) { const { userId: targetUserId, updatedByUserId: userId, role, client, occurredAt } = UserAnonymizedEventLoggingJob; diff --git a/api/src/shared/application/jobs/job-controller.js b/api/src/shared/application/jobs/job-controller.js new file mode 100644 index 00000000000..2df51090be3 --- /dev/null +++ b/api/src/shared/application/jobs/job-controller.js @@ -0,0 +1,9 @@ +export class JobController { + constructor(jobName) { + this.jobName = jobName; + } + + isJobEnabled() { + return true; + } +} diff --git a/api/src/shared/application/jobs/lcms-refresh-cache-job-controller.js b/api/src/shared/application/jobs/lcms-refresh-cache-job-controller.js index 6b913fb0e6b..173067f7d16 100644 --- a/api/src/shared/application/jobs/lcms-refresh-cache-job-controller.js +++ b/api/src/shared/application/jobs/lcms-refresh-cache-job-controller.js @@ -1,6 +1,12 @@ +import { LcmsRefreshCacheJob } from '../../domain/models/LcmsRefreshCacheJob.js'; import * as learningContentDatasource from '../../infrastructure/datasources/learning-content/datasource.js'; +import { JobController } from './job-controller.js'; + +export class LcmsRefreshCacheJobController extends JobController { + constructor() { + super(LcmsRefreshCacheJob.name); + } -export class LcmsRefreshCacheJobController { async handle(_, dependencies = { learningContentDatasource }) { await dependencies.learningContentDatasource.refreshLearningContentCacheRecords(); } diff --git a/api/src/shared/infrastructure/jobs/JobPriority.js b/api/src/shared/infrastructure/jobs/JobPriority.js deleted file mode 100644 index 500a9c3731e..00000000000 --- a/api/src/shared/infrastructure/jobs/JobPriority.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Job priority. Higher numbers have, um, higher priority - * @see https://github.com/timgit/pg-boss/blob/master/docs/readme.md#insertjobs - * @readonly - * @enum {number} - */ -export const JobPriority = Object.freeze({ - DEFAULT: 0, - HIGH: 1, -}); diff --git a/api/src/shared/infrastructure/jobs/JobQueue.js b/api/src/shared/infrastructure/jobs/JobQueue.js index de623712dd7..91188286f39 100644 --- a/api/src/shared/infrastructure/jobs/JobQueue.js +++ b/api/src/shared/infrastructure/jobs/JobQueue.js @@ -1,7 +1,7 @@ import { config } from '../../config.js'; import { logger } from '../utils/logger.js'; import { MonitoredJobHandler } from './monitoring/MonitoredJobHandler.js'; - +import { MonitoringJobExecutionTimeHandler } from './monitoring/MonitoringJobExecutionTimeHandler.js'; const { teamSize, teamConcurrency } = config.pgBoss; class JobQueue { @@ -9,12 +9,17 @@ class JobQueue { this.pgBoss = pgBoss; } - performJob(name, handlerClass, dependencies) { + registerJob(name, handlerClass, dependencies) { this.pgBoss.work(name, { teamSize, teamConcurrency }, async (job) => { const jobHandler = new handlerClass({ ...dependencies, logger }); const monitoredJobHandler = new MonitoredJobHandler(jobHandler, logger); return monitoredJobHandler.handle(job.data, name); }); + + this.pgBoss.onComplete(name, { teamSize, teamConcurrency }, (job) => { + const monitoringJobHandler = new MonitoringJobExecutionTimeHandler({ logger }); + monitoringJobHandler.handle(job); + }); } async stop() { diff --git a/api/src/shared/infrastructure/jobs/monitoring/MonitoredJobQueue.js b/api/src/shared/infrastructure/jobs/monitoring/MonitoredJobQueue.js deleted file mode 100644 index d7ff8914265..00000000000 --- a/api/src/shared/infrastructure/jobs/monitoring/MonitoredJobQueue.js +++ /dev/null @@ -1,26 +0,0 @@ -import { config } from '../../../config.js'; -import { logger } from '../../utils/logger.js'; -import { MonitoringJobExecutionTimeHandler } from './MonitoringJobExecutionTimeHandler.js'; - -const { teamSize, teamConcurrency } = config.pgBoss; - -class MonitoredJobQueue { - constructor(jobQueue) { - this.jobQueue = jobQueue; - } - - performJob(name, handlerClass, dependencies) { - this.jobQueue.performJob(name, handlerClass, dependencies); - - const monitoringJobHandler = new MonitoringJobExecutionTimeHandler({ logger }); - this.jobQueue.pgBoss.onComplete(name, { teamSize, teamConcurrency }, (job) => { - monitoringJobHandler.handle(job); - }); - } - - async stop() { - await this.jobQueue.stop(); - } -} - -export { MonitoredJobQueue }; diff --git a/api/src/shared/infrastructure/jobs/JobPgBoss.js b/api/src/shared/infrastructure/repositories/jobs/job-repository.js similarity index 76% rename from api/src/shared/infrastructure/jobs/JobPgBoss.js rename to api/src/shared/infrastructure/repositories/jobs/job-repository.js index 1a311ab7a4e..a4a4ee296a0 100644 --- a/api/src/shared/infrastructure/jobs/JobPgBoss.js +++ b/api/src/shared/infrastructure/repositories/jobs/job-repository.js @@ -1,10 +1,9 @@ import Joi from 'joi'; -import { DomainTransaction } from '../../domain/DomainTransaction.js'; -import { EntityValidationError } from '../../domain/errors.js'; -import { JobPriority } from './JobPriority.js'; +import { DomainTransaction } from '../../../domain/DomainTransaction.js'; +import { EntityValidationError } from '../../../domain/errors.js'; -class JobPgBoss { +export class JobRepository { #schema = Joi.object({ priority: Joi.string() .required() @@ -61,4 +60,13 @@ class JobPgBoss { } } -export { JobPgBoss }; +/** + * Job priority. Higher numbers have, um, higher priority + * @see https://github.com/timgit/pg-boss/blob/master/docs/readme.md#insertjobs + * @readonly + * @enum {number} + */ +export const JobPriority = Object.freeze({ + DEFAULT: 0, + HIGH: 1, +}); diff --git a/api/src/shared/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository.js b/api/src/shared/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository.js index fe6ec14988f..9783792c5d7 100644 --- a/api/src/shared/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository.js +++ b/api/src/shared/infrastructure/repositories/jobs/lcms-refresh-cache-job-repository.js @@ -1,7 +1,7 @@ import { LcmsRefreshCacheJob } from '../../../domain/models/LcmsRefreshCacheJob.js'; -import { JobPgBoss } from '../../jobs/JobPgBoss.js'; +import { JobRepository } from './job-repository.js'; -class LcmsRefreshCacheJobRepository extends JobPgBoss { +class LcmsRefreshCacheJobRepository extends JobRepository { constructor() { super({ name: LcmsRefreshCacheJob.name, diff --git a/api/src/shared/infrastructure/utils/import-named-exports-from-directory.js b/api/src/shared/infrastructure/utils/import-named-exports-from-directory.js index 86d3202b34b..54c0a9682dc 100644 --- a/api/src/shared/infrastructure/utils/import-named-exports-from-directory.js +++ b/api/src/shared/infrastructure/utils/import-named-exports-from-directory.js @@ -33,3 +33,16 @@ export async function importNamedExportsFromDirectory({ path, ignoredFileNames = } return imports; } + +export async function importNamedExportFromFile(filepath) { + const fileURL = pathToFileURL(filepath); + const module = await import(fileURL); + const namedExports = Object.entries(module); + + return namedExports + .filter(([exportName]) => exportName !== 'default') + .reduce((exports, [exportName, exportedValue]) => { + exports[exportName] = exportedValue; + return exports; + }, {}); +} diff --git a/api/tests/identity-access-management/unit/application/jobs/gar-anonymized-batch-events-logging-job.controller.test.js b/api/tests/identity-access-management/unit/application/jobs/gar-anonymized-batch-events-logging.job-controller.test.js similarity index 97% rename from api/tests/identity-access-management/unit/application/jobs/gar-anonymized-batch-events-logging-job.controller.test.js rename to api/tests/identity-access-management/unit/application/jobs/gar-anonymized-batch-events-logging.job-controller.test.js index 82d78281f50..aeb268130fe 100644 --- a/api/tests/identity-access-management/unit/application/jobs/gar-anonymized-batch-events-logging-job.controller.test.js +++ b/api/tests/identity-access-management/unit/application/jobs/gar-anonymized-batch-events-logging.job-controller.test.js @@ -1,4 +1,4 @@ -import { GarAnonymizedBatchEventsLoggingJobController } from '../../../../../src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging-job.controller.js'; +import { GarAnonymizedBatchEventsLoggingJobController } from '../../../../../src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging.job-controller.js'; import { GarAnonymizedBatchEventsLoggingJob } from '../../../../../src/identity-access-management/domain/models/GarAnonymizedBatchEventsLoggingJob.js'; import { expect, sinon } from '../../../../test-helper.js'; describe('Unit | Prescription | Application | Jobs | garAnonymizedBatchEventsLoggingJobController', function () { diff --git a/api/tests/integration/infrastructure/repositories/jobs/certification-completed-job-repository_test.js b/api/tests/integration/infrastructure/repositories/jobs/certification-completed-job-repository_test.js index 480744accf0..cf90f2e0891 100644 --- a/api/tests/integration/infrastructure/repositories/jobs/certification-completed-job-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/jobs/certification-completed-job-repository_test.js @@ -1,7 +1,7 @@ import { CertificationCompletedJob } from '../../../../../lib/domain/events/CertificationCompleted.js'; import { certificationCompletedJobRepository } from '../../../../../lib/infrastructure/repositories/jobs/certification-completed-job-repository.js'; import { LOCALE } from '../../../../../src/shared/domain/constants.js'; -import { JobPriority } from '../../../../../src/shared/infrastructure/jobs/JobPriority.js'; +import { JobPriority } from '../../../../../src/shared/infrastructure/repositories/jobs/job-repository.js'; import { expect } from '../../../../test-helper.js'; describe('Integration | Repository | Jobs | CertificationCompletedJobRepository', function () { diff --git a/api/tests/prescription/learner-management/unit/application/jobs/import-organization-learners-job-controller_test.js b/api/tests/prescription/learner-management/unit/application/jobs/import-organization-learners-job-controller_test.js index 49d5ed4d9fd..c760aeb96a2 100644 --- a/api/tests/prescription/learner-management/unit/application/jobs/import-organization-learners-job-controller_test.js +++ b/api/tests/prescription/learner-management/unit/application/jobs/import-organization-learners-job-controller_test.js @@ -1,11 +1,37 @@ import { ImportOrganizationLearnersJobController } from '../../../../../../src/prescription/learner-management/application/jobs/import-organization-learners-job-controller.js'; import { usecases } from '../../../../../../src/prescription/learner-management/domain/usecases/index.js'; +import { config } from '../../../../../../src/shared/config.js'; import { expect, sinon } from '../../../../../test-helper.js'; describe('Unit | Prescription | Application | Jobs | importOrganizationLearnersJobController', function () { + describe('#isJobEnabled', function () { + it('return true when job is enabled', function () { + //given + sinon.stub(config.pgBoss, 'importFileJobEnabled').value(true); + + // when + const handler = new ImportOrganizationLearnersJobController(); + + // then + expect(handler.isJobEnabled()).to.be.true; + }); + + it('return false when job is disabled', function () { + //given + sinon.stub(config.pgBoss, 'importFileJobEnabled').value(false); + + //when + const handler = new ImportOrganizationLearnersJobController(); + + //then + expect(handler.isJobEnabled()).to.be.false; + }); + }); + describe('#handle', function () { it('should call usecase', async function () { sinon.stub(usecases, 'addOrUpdateOrganizationLearners'); + // given const handler = new ImportOrganizationLearnersJobController(); const computeImportOrganizationLearnersJob = { organizationImportId: Symbol('organizationImportId') }; diff --git a/api/tests/prescription/learner-management/unit/application/jobs/validate-organization-learners-import-file-job-controller_test.js b/api/tests/prescription/learner-management/unit/application/jobs/validate-organization-learners-import-file-job-controller_test.js index d0e5fe2d29e..f7debdc8050 100644 --- a/api/tests/prescription/learner-management/unit/application/jobs/validate-organization-learners-import-file-job-controller_test.js +++ b/api/tests/prescription/learner-management/unit/application/jobs/validate-organization-learners-import-file-job-controller_test.js @@ -1,8 +1,33 @@ import { ValidateOrganizationLearnersImportFileJobController } from '../../../../../../src/prescription/learner-management/application/jobs/validate-organization-learners-import-file-job-controller.js'; import { usecases } from '../../../../../../src/prescription/learner-management/domain/usecases/index.js'; +import { config } from '../../../../../../src/shared/config.js'; import { expect, sinon } from '../../../../../test-helper.js'; describe('Unit | Prescription | Application | Jobs | validateOrganizationLearnersImportFileJobController', function () { + describe('#isJobEnabled', function () { + it('return true when job is enabled', function () { + //given + sinon.stub(config.pgBoss, 'validationFileJobEnabled').value(true); + + // when + const handler = new ValidateOrganizationLearnersImportFileJobController(); + + // then + expect(handler.isJobEnabled()).to.be.true; + }); + + it('return false when job is disabled', function () { + //given + sinon.stub(config.pgBoss, 'validationFileJobEnabled').value(false); + + //when + const handler = new ValidateOrganizationLearnersImportFileJobController(); + + //then + expect(handler.isJobEnabled()).to.be.false; + }); + }); + describe('#handle', function () { it('should call usecase', async function () { sinon.stub(usecases, 'validateSiecleXmlFile'); diff --git a/api/tests/shared/integration/infrastructure/jobs/JobQueue_test.js b/api/tests/shared/integration/infrastructure/jobs/JobQueue_test.js index 65df9b52b8e..46ce9813866 100644 --- a/api/tests/shared/integration/infrastructure/jobs/JobQueue_test.js +++ b/api/tests/shared/integration/infrastructure/jobs/JobQueue_test.js @@ -1,14 +1,14 @@ import PgBoss from 'pg-boss'; -import { JobPgBoss as Job } from '../../../../../src/shared/infrastructure/jobs/JobPgBoss.js'; import { JobQueue } from '../../../../../src/shared/infrastructure/jobs/JobQueue.js'; +import { JobRepository } from '../../../../../src/shared/infrastructure/repositories/jobs/job-repository.js'; import { expect } from '../../../../test-helper.js'; describe('Integration | Infrastructure | Jobs | JobQueue', function () { it('executes job when a job is added to the queue', async function () { const name = 'JobTest'; const expectedParams = { jobParam: 1 }; - const job = new Job({ name }); + const job = new JobRepository({ name }); await job.performAsync(expectedParams); const pgBoss = new PgBoss(process.env.TEST_DATABASE_URL); await pgBoss.start(); @@ -27,7 +27,7 @@ describe('Integration | Infrastructure | Jobs | JobQueue', function () { } }; - jobQueue.performJob(name, handler); + jobQueue.registerJob(name, handler); }); return promise; diff --git a/api/tests/shared/integration/infrastructure/jobs/JobPgBoss_test.js b/api/tests/shared/integration/infrastructure/repositories/jobs/job-repository_test.js similarity index 69% rename from api/tests/shared/integration/infrastructure/jobs/JobPgBoss_test.js rename to api/tests/shared/integration/infrastructure/repositories/jobs/job-repository_test.js index bafc3b1fd27..968ad0743db 100644 --- a/api/tests/shared/integration/infrastructure/jobs/JobPgBoss_test.js +++ b/api/tests/shared/integration/infrastructure/repositories/jobs/job-repository_test.js @@ -1,9 +1,11 @@ -import { EntityValidationError } from '../../../../../src/shared/domain/errors.js'; -import { JobPgBoss as Job } from '../../../../../src/shared/infrastructure/jobs/JobPgBoss.js'; -import { JobPriority } from '../../../../../src/shared/infrastructure/jobs/JobPriority.js'; -import { catchErrSync, expect, knex } from '../../../../test-helper.js'; +import { EntityValidationError } from '../../../../../../src/shared/domain/errors.js'; +import { + JobPriority, + JobRepository, +} from '../../../../../../src/shared/infrastructure/repositories/jobs/job-repository.js'; +import { catchErrSync, expect, knex } from '../../../../../test-helper.js'; -describe('Integration | Infrastructure | Jobs | JobPgBoss', function () { +describe('Integration | Infrastructure | Repositories | Jobs | job-repository', function () { it('create one job db with given config', async function () { // given const name = 'JobTest'; @@ -14,7 +16,7 @@ describe('Integration | Infrastructure | Jobs | JobPgBoss', function () { const expireIn = '00:00:30'; const priority = JobPriority.HIGH; - const job = new Job({ name, retryLimit, retryDelay, retryBackoff, expireIn, priority }, knex); + const job = new JobRepository({ name, retryLimit, retryDelay, retryBackoff, expireIn, priority }, knex); // when await job.performAsync(expectedParams); @@ -40,7 +42,7 @@ describe('Integration | Infrastructure | Jobs | JobPgBoss', function () { const expireIn = '00:00:30'; const priority = JobPriority.HIGH; - const job = new Job({ name, retryLimit, retryDelay, retryBackoff, expireIn, priority }, knex); + const job = new JobRepository({ name, retryLimit, retryDelay, retryBackoff, expireIn, priority }, knex); // when await job.performAsync(...expectedParams); @@ -59,7 +61,7 @@ describe('Integration | Infrastructure | Jobs | JobPgBoss', function () { const expireIn = '00:00:30'; const priority = JobPriority.HIGH; - const job = new Job({ name, retryLimit, retryDelay, retryBackoff, expireIn, priority }); + const job = new JobRepository({ name, retryLimit, retryDelay, retryBackoff, expireIn, priority }); // when const jobsInserted = await job.performAsync(...expectedParams); @@ -73,7 +75,7 @@ describe('Integration | Infrastructure | Jobs | JobPgBoss', function () { const priority = 999; // when - const error = catchErrSync(({ priority }, knex) => new Job({ priority }, knex))({ priority }, knex); + const error = catchErrSync(({ priority }, knex) => new JobRepository({ priority }, knex))({ priority }, knex); // then expect(error).to.be.instanceOf(EntityValidationError); diff --git a/api/tests/tooling/jobs/expect-job.test.js b/api/tests/tooling/jobs/expect-job.test.js index 7bce9f0dc15..351a3f0930b 100644 --- a/api/tests/tooling/jobs/expect-job.test.js +++ b/api/tests/tooling/jobs/expect-job.test.js @@ -1,12 +1,12 @@ -import { JobPgBoss } from '../../../src/shared/infrastructure/jobs/JobPgBoss.js'; +import { JobRepository } from '../../../src/shared/infrastructure/repositories/jobs/job-repository.js'; import { catchErr, expect, knex } from '../../test-helper.js'; describe('Integration | Tooling | Expect Job', function () { describe('#withJobsCount', function () { it('succeeds when count of executed jobs is correct', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); - const job2 = new JobPgBoss({ name: 'JobTest2' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); + const job2 = new JobRepository({ name: 'JobTest2' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -21,7 +21,7 @@ describe('Integration | Tooling | Expect Job', function () { it('fails when count of executed jobs is not correct', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -39,7 +39,7 @@ describe('Integration | Tooling | Expect Job', function () { describe('#withJob', function () { it('succeeds when the full job data is the same', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -57,7 +57,7 @@ describe('Integration | Tooling | Expect Job', function () { it('fails when the full job data is not the same', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -71,7 +71,7 @@ describe('Integration | Tooling | Expect Job', function () { it('fails when multiple jobs are triggered instead of 1', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -90,7 +90,7 @@ describe('Integration | Tooling | Expect Job', function () { describe('#withJobPayloads', function () { it('succeeds when the jobs payloads are correct', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -102,7 +102,7 @@ describe('Integration | Tooling | Expect Job', function () { it('fails when not all job payloads are correct', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -118,7 +118,7 @@ describe('Integration | Tooling | Expect Job', function () { it('fails when not all jobs are executed', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -137,8 +137,8 @@ describe('Integration | Tooling | Expect Job', function () { describe('#withJobPayload', function () { it('succeeds when the job payload is correct', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); - const job2 = new JobPgBoss({ name: 'JobTest2' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); + const job2 = new JobRepository({ name: 'JobTest2' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -151,7 +151,7 @@ describe('Integration | Tooling | Expect Job', function () { it('fails when the job payload is not correct', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); @@ -165,7 +165,7 @@ describe('Integration | Tooling | Expect Job', function () { it('fails when multiple jobs are triggered instead of 1', async function () { // given - const job = new JobPgBoss({ name: 'JobTest' }, knex); + const job = new JobRepository({ name: 'JobTest' }, knex); // when await job.performAsync({ foo: 'bar' }); diff --git a/api/tests/unit/worker_test.js b/api/tests/unit/worker_test.js index 7f13e89e9be..6d60bb87e74 100644 --- a/api/tests/unit/worker_test.js +++ b/api/tests/unit/worker_test.js @@ -1,239 +1,72 @@ -import { CertificationCompletedJob } from '../../lib/domain/events/CertificationCompleted.js'; -import { CertificationCompletedJobController } from '../../src/certification/scoring/application/jobs/certification-completed-job-controller.js'; -import { CertificationRescoringByScriptJobController } from '../../src/certification/session-management/application/jobs/certification-rescoring-by-script-job-controller.js'; -import { CertificationRescoringByScriptJob } from '../../src/certification/session-management/domain/models/CertificationRescoringByScriptJob.js'; import { UserAnonymizedEventLoggingJob } from '../../src/identity-access-management/domain/models/UserAnonymizedEventLoggingJob.js'; -import { ParticipationResultCalculationJobController } from '../../src/prescription/campaign-participation/application/jobs/participation-result-calculation-job-controller.js'; -import { PoleEmploiParticipationCompletedJobController } from '../../src/prescription/campaign-participation/application/jobs/pole-emploi-participation-completed-job-controller.js'; -import { ParticipationResultCalculationJob } from '../../src/prescription/campaign-participation/domain/models/ParticipationResultCalculationJob.js'; -import { PoleEmploiParticipationCompletedJob } from '../../src/prescription/campaign-participation/domain/models/PoleEmploiParticipationCompletedJob.js'; -import { ComputeCertificabilityJobController } from '../../src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js'; -import { ImportOrganizationLearnersJobController } from '../../src/prescription/learner-management/application/jobs/import-organization-learners-job-controller.js'; import { ValidateOrganizationLearnersImportFileJobController } from '../../src/prescription/learner-management/application/jobs/validate-organization-learners-import-file-job-controller.js'; -import { ComputeCertificabilityJob } from '../../src/prescription/learner-management/domain/models/ComputeCertificabilityJob.js'; -import { ImportOrganizationLearnersJob } from '../../src/prescription/learner-management/domain/models/ImportOrganizationLearnersJob.js'; import { ValidateOrganizationImportFileJob } from '../../src/prescription/learner-management/domain/models/ValidateOrganizationImportFileJob.js'; import { UserAnonymizedEventLoggingJobController } from '../../src/shared/application/jobs/audit-log/user-anonymized-event-logging-job-controller.js'; -import { LcmsRefreshCacheJobController } from '../../src/shared/application/jobs/lcms-refresh-cache-job-controller.js'; import { config } from '../../src/shared/config.js'; -import { LcmsRefreshCacheJob } from '../../src/shared/domain/models/LcmsRefreshCacheJob.js'; -import { runJobs } from '../../worker.js'; +import { registerJobs } from '../../worker.js'; import { expect, sinon } from '../test-helper.js'; -describe('#runjobs', function () { - let startPgBossStub, createMonitoredJobQueueStub, scheduleCpfJobsStub, monitoredJobQueueStub; +describe('#registerJobs', function () { + let startPgBossStub, createJobQueueStub, scheduleCpfJobsStub, jobQueueStub; beforeEach(function () { const pgBossStub = { schedule: sinon.stub() }; - monitoredJobQueueStub = { performJob: sinon.stub() }; + jobQueueStub = { registerJob: sinon.stub() }; startPgBossStub = sinon.stub(); startPgBossStub.resolves(pgBossStub); - createMonitoredJobQueueStub = sinon.stub(); - createMonitoredJobQueueStub.withArgs(pgBossStub).returns(monitoredJobQueueStub); + createJobQueueStub = sinon.stub(); + createJobQueueStub.withArgs(pgBossStub).returns(jobQueueStub); scheduleCpfJobsStub = sinon.stub(); }); - describe('Acces', function () { - it('should register UserAnonymizedEventLoggingJob', async function () { - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === UserAnonymizedEventLoggingJob.name); - - expect(calls.args[1]).to.equal(UserAnonymizedEventLoggingJobController); + it('should register UserAnonymizedEventLoggingJob', async function () { + // when + await registerJobs({ + startPgBoss: startPgBossStub, + createJobQueue: createJobQueueStub, + scheduleCpfJobs: scheduleCpfJobsStub, }); - }); - - describe('Contenu', function () { - it('should register LcmsRefreshCacheJob', async function () { - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === LcmsRefreshCacheJob.name); - expect(calls.args[1]).to.equal(LcmsRefreshCacheJobController); - }); + // then + expect(jobQueueStub.registerJob).to.have.been.calledWithExactly( + UserAnonymizedEventLoggingJob.name, + UserAnonymizedEventLoggingJobController, + ); }); - describe('Certification', function () { - it('should register CertificationCompletedJob', async function () { - //given - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === CertificationCompletedJob.name); + it('should register ValidateOrganizationImportFileJob when job is enabled', async function () { + //given + sinon.stub(config.pgBoss, 'validationFileJobEnabled').value(true); - expect(calls.args[1]).to.equal(CertificationCompletedJobController); + // when + await registerJobs({ + startPgBoss: startPgBossStub, + createJobQueue: createJobQueueStub, + scheduleCpfJobs: scheduleCpfJobsStub, }); - it('should register CertificationRescoringByScriptJob', async function () { - //given - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === CertificationRescoringByScriptJob.name); - - expect(calls.args[1]).to.equal(CertificationRescoringByScriptJobController); - }); + // then + expect(jobQueueStub.registerJob).to.have.been.calledWithExactly( + ValidateOrganizationImportFileJob.name, + ValidateOrganizationLearnersImportFileJobController, + ); }); - describe('Prescription', function () { - it('should register CampaignParticipationCompletedJob', async function () { - //given - - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === PoleEmploiParticipationCompletedJob.name); - - expect(calls.args[1]).to.equal(PoleEmploiParticipationCompletedJobController); - }); - - it('should register ComputeCertificabilityJob', async function () { - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === ComputeCertificabilityJob.name); - - expect(calls.args[1]).to.equal(ComputeCertificabilityJobController); - }); - - it('should register ParticipationResultCalculationJob', async function () { - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === ParticipationResultCalculationJob.name); + it('should not register ValidateOrganizationImportFileJob when job is disabled', async function () { + //given + sinon.stub(config.pgBoss, 'validationFileJobEnabled').value(false); - expect(calls.args[1]).to.equal(ParticipationResultCalculationJobController); + // when + await registerJobs({ + startPgBoss: startPgBossStub, + createJobQueue: createJobQueueStub, + scheduleCpfJobs: scheduleCpfJobsStub, }); - describe('Import jobs', function () { - it('should register ValidateOrganizationImportFileJob', async function () { - //given - sinon.stub(config.pgBoss, 'validationFileJobEnabled').value(true); - - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === ValidateOrganizationImportFileJob.name); - - expect(calls.args[1]).to.equal(ValidateOrganizationLearnersImportFileJobController); - }); - - it('should not register import job if PGBOSS_IMPORT_FILE_JOB_ENABLED is false', async function () { - //given - sinon.stub(config.pgBoss, 'importFileJobEnabled').value(false); - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob.getCalls(); - - const validatationJob = calls.find(({ args }) => args[0] === ValidateOrganizationImportFileJob.name); - const importJob = calls.find(({ args }) => args[0] === ImportOrganizationLearnersJob.name); - - expect(validatationJob).to.exist; - expect(importJob).to.be.undefined; - }); - - it('should not register validation job if PGBOSS_VALIDATION_FILE_JOB_ENABLED is false', async function () { - //given - sinon.stub(config.pgBoss, 'validationFileJobEnabled').value(false); - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob.getCalls(); - - const validatationJob = calls.find(({ args }) => args[0] === ValidateOrganizationImportFileJob.name); - const importJob = calls.find(({ args }) => args[0] === ImportOrganizationLearnersJob.name); - - expect(validatationJob).to.be.undefined; - expect(importJob).to.exist; - }); - - it('should register ImportOrganizationLearnersJob', async function () { - //given - sinon.stub(config.pgBoss, 'importFileJobEnabled').value(true); - - // when - await runJobs({ - startPgBoss: startPgBossStub, - createMonitoredJobQueue: createMonitoredJobQueueStub, - scheduleCpfJobs: scheduleCpfJobsStub, - }); - - // then - const calls = monitoredJobQueueStub.performJob - .getCalls() - .find(({ args }) => args[0] === ImportOrganizationLearnersJob.name); - - expect(calls.args[1]).to.equal(ImportOrganizationLearnersJobController); - }); - }); + // then + expect(jobQueueStub.registerJob).to.not.have.been.calledWithExactly( + ValidateOrganizationImportFileJob.name, + ValidateOrganizationLearnersImportFileJobController, + ); }); }); diff --git a/api/worker.js b/api/worker.js index 7e5cb0e5cf3..9c1dfea7962 100644 --- a/api/worker.js +++ b/api/worker.js @@ -1,42 +1,22 @@ import 'dotenv/config'; -import * as url from 'node:url'; +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { glob } from 'glob'; import _ from 'lodash'; import PgBoss from 'pg-boss'; -import { CertificationCompletedJob } from './lib/domain/events/CertificationCompleted.js'; -import { CertificationCompletedJobController } from './src/certification/scoring/application/jobs/certification-completed-job-controller.js'; -import { CertificationRescoringByScriptJobController } from './src/certification/session-management/application/jobs/certification-rescoring-by-script-job-controller.js'; -import { CertificationRescoringByScriptJob } from './src/certification/session-management/domain/models/CertificationRescoringByScriptJob.js'; -import { GarAnonymizedBatchEventsLoggingJobController } from './src/identity-access-management/application/jobs/gar-anonymized-batch-events-logging-job.controller.js'; -import { GarAnonymizedBatchEventsLoggingJob } from './src/identity-access-management/domain/models/GarAnonymizedBatchEventsLoggingJob.js'; -import { UserAnonymizedEventLoggingJob } from './src/identity-access-management/domain/models/UserAnonymizedEventLoggingJob.js'; -import { ParticipationResultCalculationJobController } from './src/prescription/campaign-participation/application/jobs/participation-result-calculation-job-controller.js'; -import { PoleEmploiParticipationCompletedJobController } from './src/prescription/campaign-participation/application/jobs/pole-emploi-participation-completed-job-controller.js'; -import { PoleEmploiParticipationStartedJobController } from './src/prescription/campaign-participation/application/jobs/pole-emploi-participation-started-job-controller.js'; -import { SendSharedParticipationResultsToPoleEmploiJobController } from './src/prescription/campaign-participation/application/jobs/send-share-participation-results-to-pole-emploi-job-controller.js'; -import { ParticipationResultCalculationJob } from './src/prescription/campaign-participation/domain/models/ParticipationResultCalculationJob.js'; -import { PoleEmploiParticipationCompletedJob } from './src/prescription/campaign-participation/domain/models/PoleEmploiParticipationCompletedJob.js'; -import { PoleEmploiParticipationStartedJob } from './src/prescription/campaign-participation/domain/models/PoleEmploiParticipationStartedJob.js'; -import { SendSharedParticipationResultsToPoleEmploiJob } from './src/prescription/campaign-participation/domain/models/SendSharedParticipationResultsToPoleEmploiJob.js'; -import { ComputeCertificabilityJobController } from './src/prescription/learner-management/application/jobs/compute-certificability-job-controller.js'; -import { ImportOrganizationLearnersJobController } from './src/prescription/learner-management/application/jobs/import-organization-learners-job-controller.js'; -import { ScheduleComputeOrganizationLearnersCertificabilityJobController } from './src/prescription/learner-management/application/jobs/schedule-compute-organization-learners-certificability-job-controller.js'; -import { ValidateOrganizationLearnersImportFileJobController } from './src/prescription/learner-management/application/jobs/validate-organization-learners-import-file-job-controller.js'; -import { ComputeCertificabilityJob } from './src/prescription/learner-management/domain/models/ComputeCertificabilityJob.js'; -import { ImportOrganizationLearnersJob } from './src/prescription/learner-management/domain/models/ImportOrganizationLearnersJob.js'; import { ScheduleComputeOrganizationLearnersCertificabilityJob } from './src/prescription/learner-management/domain/models/ScheduleComputeOrganizationLearnersCertificabilityJob.js'; -import { ValidateOrganizationImportFileJob } from './src/prescription/learner-management/domain/models/ValidateOrganizationImportFileJob.js'; -import { UserAnonymizedEventLoggingJobController } from './src/shared/application/jobs/audit-log/user-anonymized-event-logging-job-controller.js'; -import { LcmsRefreshCacheJobController } from './src/shared/application/jobs/lcms-refresh-cache-job-controller.js'; import { config } from './src/shared/config.js'; -import { LcmsRefreshCacheJob } from './src/shared/domain/models/LcmsRefreshCacheJob.js'; import { scheduleCpfJobs } from './src/shared/infrastructure/jobs/cpf-export/schedule-cpf-jobs.js'; import { JobQueue } from './src/shared/infrastructure/jobs/JobQueue.js'; -import { MonitoredJobQueue } from './src/shared/infrastructure/jobs/monitoring/MonitoredJobQueue.js'; +import { importNamedExportFromFile } from './src/shared/infrastructure/utils/import-named-exports-from-directory.js'; import { logger } from './src/shared/infrastructure/utils/logger.js'; +const workerPath = fileURLToPath(import.meta.url); +const workerDirPath = dirname(workerPath); + async function startPgBoss() { logger.info('Starting pg-boss'); const monitorStateIntervalSeconds = config.pgBoss.monitorStateIntervalSeconds; @@ -62,11 +42,10 @@ async function startPgBoss() { return pgBoss; } -function createMonitoredJobQueue(pgBoss) { +function createJobQueue(pgBoss) { const jobQueue = new JobQueue(pgBoss); - const monitoredJobQueue = new MonitoredJobQueue(jobQueue); process.on('SIGINT', async () => { - await monitoredJobQueue.stop(); + await jobQueue.stop(); // Make sure pgBoss stopped before quitting pgBoss.on('stopped', () => { @@ -74,53 +53,38 @@ function createMonitoredJobQueue(pgBoss) { process.exit(0); }); }); - return monitoredJobQueue; + return jobQueue; } -export async function runJobs(dependencies = { startPgBoss, createMonitoredJobQueue, scheduleCpfJobs }) { +export async function registerJobs(dependencies = { startPgBoss, createJobQueue, scheduleCpfJobs }) { const pgBoss = await dependencies.startPgBoss(); - const monitoredJobQueue = dependencies.createMonitoredJobQueue(pgBoss); - - // Access - monitoredJobQueue.performJob(UserAnonymizedEventLoggingJob.name, UserAnonymizedEventLoggingJobController); - monitoredJobQueue.performJob(GarAnonymizedBatchEventsLoggingJob.name, GarAnonymizedBatchEventsLoggingJobController); - - // Contenu - monitoredJobQueue.performJob(LcmsRefreshCacheJob.name, LcmsRefreshCacheJobController); + const jobQueue = dependencies.createJobQueue(pgBoss); - // Prescription - monitoredJobQueue.performJob(ComputeCertificabilityJob.name, ComputeCertificabilityJobController); - monitoredJobQueue.performJob( - ScheduleComputeOrganizationLearnersCertificabilityJob.name, - ScheduleComputeOrganizationLearnersCertificabilityJobController, - ); - monitoredJobQueue.performJob(ParticipationResultCalculationJob.name, ParticipationResultCalculationJobController); + const globPattern = `${workerDirPath}/src/**/application/**/*job-controller.js`; - monitoredJobQueue.performJob(PoleEmploiParticipationCompletedJob.name, PoleEmploiParticipationCompletedJobController); - monitoredJobQueue.performJob( - SendSharedParticipationResultsToPoleEmploiJob.name, - SendSharedParticipationResultsToPoleEmploiJobController, - ); - monitoredJobQueue.performJob(PoleEmploiParticipationStartedJob.name, PoleEmploiParticipationStartedJobController); + logger.info(`Search for job handlers in ${globPattern}`); + const jobFiles = await glob(globPattern, { ignore: '**/job-controller.js' }); - if (config.pgBoss.importFileJobEnabled) { - monitoredJobQueue.performJob(ImportOrganizationLearnersJob.name, ImportOrganizationLearnersJobController); + logger.info(`${jobFiles.length} job handlers files found.`); + let jobModules = {}; + for (const jobFile of jobFiles) { + const fileModules = await importNamedExportFromFile(jobFile); + jobModules = { ...jobModules, ...fileModules }; } - if (config.pgBoss.validationFileJobEnabled) { - monitoredJobQueue.performJob( - ValidateOrganizationImportFileJob.name, - ValidateOrganizationLearnersImportFileJobController, - ); - } + for (const [moduleName, ModuleClass] of Object.entries(jobModules)) { + const job = new ModuleClass(); - //Certification - monitoredJobQueue.performJob(CertificationRescoringByScriptJob.name, CertificationRescoringByScriptJobController); - monitoredJobQueue.performJob(CertificationCompletedJob.name, CertificationCompletedJobController); + if (job.isJobEnabled()) { + logger.info(`Job "${job.jobName}" registered from module "${moduleName}."`); + jobQueue.registerJob(job.jobName, ModuleClass); + } else { + logger.warn(`Job "${job.jobName}" is disabled.`); + } + } // TODO - use abstraction for CRON - - //schudeler + // Scheduler await pgBoss.schedule( ScheduleComputeOrganizationLearnersCertificabilityJob.name, config.features.scheduleComputeOrganizationLearnersCertificability.cron, @@ -134,12 +98,11 @@ export async function runJobs(dependencies = { startPgBoss, createMonitoredJobQu const startInWebProcess = process.env.START_JOB_IN_WEB_PROCESS; const isTestEnv = process.env.NODE_ENV === 'test'; -const modulePath = url.fileURLToPath(import.meta.url); -const isEntryPointFromOtherFile = process.argv[1] !== modulePath; +const isEntryPointFromOtherFile = process.argv[1] !== workerPath; if (!isTestEnv) { if (!startInWebProcess || (startInWebProcess && isEntryPointFromOtherFile)) { - await runJobs(); + await registerJobs(); } else { logger.error( 'Worker process is started in the web process. Please unset the START_JOB_IN_WEB_PROCESS environment variable to start a dedicated worker process.',