From a505aaa82405de2dfa83d53538e20646d50667f6 Mon Sep 17 00:00:00 2001 From: Tyagi-Sunny Date: Fri, 20 Sep 2024 11:51:03 +0530 Subject: [PATCH 1/8] feat(subscription-service): add billing functionality to subscription service add billing functionality to subscription service BREAKING CHANGE: yes gh-34 --- package-lock.json | 1154 ++++++++--------- services/subscription-service/.gitignore | 2 +- .../loopback4-billing-0.0.1.tgz | Bin 0 -> 29337 bytes .../20240209122448-add-customer-table.js | 59 + .../sqls/20240205055601-init-up.sql | 2 + ...20240209122448-add-customer-table-down.sql | 2 + .../20240209122448-add-customer-table-up.sql | 41 + services/subscription-service/package.json | 3 +- ...-ctrl-plane-subscription-service-0.2.1.tgz | Bin 0 -> 72945 bytes .../subscription-service/src/component.ts | 83 +- .../billing-customer.controller.ts | 211 +++ .../controllers/billing-invoice.controller.ts | 227 ++++ .../billing-payment-source.controller.ts | 138 ++ .../src/controllers/index.ts | 1 + .../subscription-invoice.controller.ts | 29 + .../src/controllers/webhook.controller.ts | 36 + .../src/interceptors/index.ts | 1 + .../webhook-verifier.interceptor.ts | 51 + services/subscription-service/src/keys.ts | 16 +- .../src/models/billing-customer.model.ts | 44 + .../src/models/dto/address-dto.model.ts | 66 + .../src/models/dto/charge-dto.model.ts | 20 + .../src/models/dto/customer-dto.model.ts | 75 ++ .../src/models/dto/index.ts | 5 + .../src/models/dto/invoice-dto.model.ts | 50 + .../src/models/dto/payment-dto.model.ts | 55 + .../src/models/dto/transaction-dto.model.ts | 110 ++ .../subscription-service/src/models/index.ts | 3 + .../src/models/invoice.model.ts | 40 + .../src/models/subscription.model.ts | 7 + .../subscription-service/src/permissions.ts | 12 + .../src/providers/index.ts | 1 + .../src/providers/system-user.provider.ts | 13 + .../billing-customer.repository.ts | 42 + .../src/repositories/index.ts | 2 + .../src/repositories/invoice.repository.ts | 24 + .../repositories/subscription.repository.ts | 15 +- services/subscription-service/src/types.ts | 22 + .../src/models/dtos/subscription-dto.model.ts | 3 + .../src/permissions.ts | 13 + .../services/lead-authenticator.service.ts | 12 + .../src/services/onboarding.service.ts | 21 +- 42 files changed, 2084 insertions(+), 627 deletions(-) create mode 100644 services/subscription-service/loopback4-billing-0.0.1.tgz create mode 100644 services/subscription-service/migrations/pg/migrations/20240209122448-add-customer-table.js create mode 100644 services/subscription-service/migrations/pg/migrations/sqls/20240209122448-add-customer-table-down.sql create mode 100644 services/subscription-service/migrations/pg/migrations/sqls/20240209122448-add-customer-table-up.sql create mode 100644 services/subscription-service/sourceloop-ctrl-plane-subscription-service-0.2.1.tgz create mode 100644 services/subscription-service/src/controllers/billing-customer.controller.ts create mode 100644 services/subscription-service/src/controllers/billing-invoice.controller.ts create mode 100644 services/subscription-service/src/controllers/billing-payment-source.controller.ts create mode 100644 services/subscription-service/src/controllers/subscription-invoice.controller.ts create mode 100644 services/subscription-service/src/controllers/webhook.controller.ts create mode 100644 services/subscription-service/src/interceptors/index.ts create mode 100644 services/subscription-service/src/interceptors/webhook-verifier.interceptor.ts create mode 100644 services/subscription-service/src/models/billing-customer.model.ts create mode 100644 services/subscription-service/src/models/dto/address-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/charge-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/customer-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/index.ts create mode 100644 services/subscription-service/src/models/dto/invoice-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/payment-dto.model.ts create mode 100644 services/subscription-service/src/models/dto/transaction-dto.model.ts create mode 100644 services/subscription-service/src/models/invoice.model.ts create mode 100644 services/subscription-service/src/providers/system-user.provider.ts create mode 100644 services/subscription-service/src/repositories/billing-customer.repository.ts create mode 100644 services/subscription-service/src/repositories/invoice.repository.ts diff --git a/package-lock.json b/package-lock.json index bf1f958..caa3c67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,9 +53,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", - "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -107,12 +107,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, "dependencies": { - "@babel/types": "^7.25.0", + "@babel/types": "^7.25.6", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -233,13 +233,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "dev": true, "dependencies": { "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" @@ -332,12 +332,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.25.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -347,9 +347,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -373,16 +373,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", - "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.2", + "@babel/types": "^7.25.6", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -400,9 +400,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -414,9 +414,9 @@ } }, "node_modules/@codegenie/serverless-express": { - "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@codegenie/serverless-express/-/serverless-express-4.14.1.tgz", - "integrity": "sha512-B90/1OmA9mf9bEJnplLj7FGf+N2v2ikB68c/9W9uXmCa4ep/V00ymCiivwGLyeuzQRW33tcj4+KxZ2utfmu39Q==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/@codegenie/serverless-express/-/serverless-express-4.15.0.tgz", + "integrity": "sha512-adnKbnW1Tg5LAe0lcbyoRchu8G6+gLwP1rvgwfHvTbCwvBQNfhsgnzq4cKkLn7ZKn2sa4JZNis/Gn/2jWBWa4A==", "dev": true, "engines": { "node": ">=12" @@ -738,9 +738,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -792,9 +792,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -856,13 +856,13 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", + "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, @@ -938,9 +938,9 @@ } }, "node_modules/@isaacs/cliui/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==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -1526,17 +1526,17 @@ } }, "node_modules/@loopback/boot": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@loopback/boot/-/boot-7.0.4.tgz", - "integrity": "sha512-LWF2EOMu2U5uoJGGx6eA0fbvztWSmlmjaCag+xVbB2yAohW6QMwVXpjlk0WauGGlwmGwKfKSbGUScdFKl2VeEQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@loopback/boot/-/boot-7.0.6.tgz", + "integrity": "sha512-xMe2gLHHjkN2s0mMgaGjvSAhLe1elyk3HXcTru1sMUDKIEvJLer59yrTI57GQbsmJT/3rLe6AQhzSmzxGOcSkg==", "dependencies": { - "@loopback/model-api-builder": "^6.0.4", - "@loopback/repository": "^7.0.4", - "@loopback/service-proxy": "^7.0.4", + "@loopback/model-api-builder": "^6.0.6", + "@loopback/repository": "^7.0.6", + "@loopback/service-proxy": "^7.0.6", "@types/debug": "^4.1.12", "@types/glob": "^8.1.0", - "debug": "^4.3.5", - "glob": "^10.4.2", + "debug": "^4.3.7", + "glob": "^10.4.5", "tslib": "^2.6.3" }, "engines": { @@ -1547,23 +1547,23 @@ } }, "node_modules/@loopback/build": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@loopback/build/-/build-11.0.4.tgz", - "integrity": "sha512-Ilm7GabuRqinsyZDh/rLJc0PeY0X7gbFZ3LZnuwfok0ywrFA38DnP6/PU24A+2XkypufJOm0MaWVgiHJRGWu2w==", + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/@loopback/build/-/build-11.0.6.tgz", + "integrity": "sha512-9m2k0hkL9q6uwqFXNComp+r8Qar2GZIenlQET36nkFxkzXi/Nex+u4+0/FIo8QN0sLC56Eek9+5vt2/XVBBtJw==", "dev": true, "dependencies": { - "@loopback/eslint-config": "^15.0.3", + "@loopback/eslint-config": "^15.0.4", "@types/mocha": "^10.0.7", "@types/node": "^16.18.101", "cross-spawn": "^7.0.3", - "debug": "^4.3.5", + "debug": "^4.3.7", "eslint": "^8.57.0", "fs-extra": "^11.2.0", - "glob": "^10.4.2", + "glob": "^10.4.5", "lodash": "^4.17.21", "mocha": "^10.6.0", "nyc": "^17.0.0", - "prettier": "^3.2.5", + "prettier": "^3.3.2", "rimraf": "^5.0.7", "source-map-support": "^0.5.21", "typescript": "~5.2.2" @@ -1582,9 +1582,9 @@ } }, "node_modules/@loopback/build/node_modules/@types/node": { - "version": "16.18.104", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.104.tgz", - "integrity": "sha512-OF3keVCbfPlkzxnnDBUZJn1RiCJzKeadjiW0xTEb0G1SUJ5gDVb3qnzZr2T4uIFvsbKJbXy1v2DN7e2zaEY7jQ==", + "version": "16.18.108", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.108.tgz", + "integrity": "sha512-fj42LD82fSv6yN9C6Q4dzS+hujHj+pTv0IpRR3kI20fnYeS0ytBpjFO9OjmDowSPPt4lNKN46JLaKbCyP+BW2A==", "dev": true }, "node_modules/@loopback/build/node_modules/brace-expansion": { @@ -1621,19 +1621,6 @@ "node": ">=8" } }, - "node_modules/@loopback/build/node_modules/foreground-child": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", - "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@loopback/build/node_modules/fs-extra": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", @@ -1713,9 +1700,9 @@ } }, "node_modules/@loopback/build/node_modules/nyc": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.0.0.tgz", - "integrity": "sha512-ISp44nqNCaPugLLGGfknzQwSwt10SSS5IMoPR7GLoMAyS18Iw5js8U7ga2VF9lYuMZ42gOHr3UddZw4WZltxKg==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", + "integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==", "dev": true, "dependencies": { "@istanbuljs/load-nyc-config": "^1.0.0", @@ -1725,7 +1712,7 @@ "decamelize": "^1.2.0", "find-cache-dir": "^3.2.0", "find-up": "^4.1.0", - "foreground-child": "^2.0.0", + "foreground-child": "^3.3.0", "get-package-type": "^0.1.0", "glob": "^7.1.6", "istanbul-lib-coverage": "^3.0.0", @@ -1910,13 +1897,13 @@ } }, "node_modules/@loopback/context": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@loopback/context/-/context-7.0.4.tgz", - "integrity": "sha512-NWtXJ2mH3Akj+Qlj4hPd4RZXyIkF4vS0DOZZXsHorPFAmvMdNi32SOXjUm/Je59k5xHBMQEW53tey8P+9iJXHQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@loopback/context/-/context-7.0.6.tgz", + "integrity": "sha512-a9NvaNfSxbgm8bs75ZyqHyqlptULqehcfAXpZq8TDMHyyJ4jVk7KpAab2YwVztzJUbgeagVv1IAHL1Gz0Qwr3g==", "dependencies": { - "@loopback/metadata": "^7.0.4", + "@loopback/metadata": "^7.0.6", "@types/debug": "^4.1.12", - "debug": "^4.3.5", + "debug": "^4.3.7", "hyperid": "^3.2.0", "p-event": "^4.2.0", "tslib": "^2.6.3", @@ -1927,12 +1914,12 @@ } }, "node_modules/@loopback/core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@loopback/core/-/core-6.1.1.tgz", - "integrity": "sha512-gphIDW8sT1+0f6QqPc1h+P3l92oaMfMd1VK8DHtyCZwrbBGJkbuZC2BgzGOQyPXhgoLbIVO07shjmjgygaV2sg==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@loopback/core/-/core-6.1.3.tgz", + "integrity": "sha512-rFs16z00cavqQeXdvFRx9CnyinPTI4qWvtnDJ/PsZSCWuvG9qBbK69aub9J+PPX/SHIgHsMExB9uNjyMycnieg==", "dependencies": { - "@loopback/context": "^7.0.4", - "debug": "^4.3.5", + "@loopback/context": "^7.0.6", + "debug": "^4.3.7", "tslib": "^2.6.3" }, "engines": { @@ -1940,13 +1927,13 @@ } }, "node_modules/@loopback/eslint-config": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@loopback/eslint-config/-/eslint-config-15.0.3.tgz", - "integrity": "sha512-Rv/o0qtEXMTIhLU2IFY9Mgp2eKDv732bG3UuVPXYROrM5P3AJkCO6tuonCDDrm6w4KTAL2xoJ9V+Fzcb8opxog==", + "version": "15.0.4", + "resolved": "https://registry.npmjs.org/@loopback/eslint-config/-/eslint-config-15.0.4.tgz", + "integrity": "sha512-ZmCd/2qoSQrKzvM4pBmSJJ1KAecVvFFimGQjZgafmxe7J5Jfb1bamLoDYMcMI1DmKCRxd77nweX1PJHC4AmGtg==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "^7.10.0", - "@typescript-eslint/parser": "^7.10.0", + "@typescript-eslint/eslint-plugin": "^7.16.0", + "@typescript-eslint/parser": "^7.16.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-eslint-plugin": "^5.5.1", "eslint-plugin-mocha": "^10.4.3" @@ -1959,17 +1946,17 @@ } }, "node_modules/@loopback/express": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@loopback/express/-/express-7.0.4.tgz", - "integrity": "sha512-JraWV1WD5FHX3/8HEMfu7BC7GcS5kK/o4NwV0fuNiiv9sD+4mnWy7mgo+w3TbyfDSxq5BKspPWSoGMsUXYnwAw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@loopback/express/-/express-7.0.6.tgz", + "integrity": "sha512-zey+J7ateOHrXIv9yq7F2RlS3Hk4lkc+fAqtORCdkoEJWblQ/oe1/gm4hYQWKnO3KDQ0pktfa3EEZlGzU0mG9Q==", "dependencies": { - "@loopback/http-server": "^6.0.4", + "@loopback/http-server": "^6.0.6", "@types/body-parser": "^1.19.5", "@types/express": "^4.17.21", "@types/express-serve-static-core": "^4.17.37", "@types/http-errors": "^2.0.4", - "body-parser": "^1.20.2", - "debug": "^4.3.5", + "body-parser": "^1.20.3", + "debug": "^4.3.7", "express": "^4.19.2", "http-errors": "^2.0.0", "on-finished": "^2.4.1", @@ -1984,9 +1971,9 @@ } }, "node_modules/@loopback/filter": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@loopback/filter/-/filter-5.0.4.tgz", - "integrity": "sha512-16SdyFe+mh8b6JIgbnS1NIiop+6vhh62BQ0hOBfRVnffCf1NEE3mrERNsbMNou9eMx/OOIBMfhoAoFwfSfyJdw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@loopback/filter/-/filter-5.0.6.tgz", + "integrity": "sha512-rGQaVrUtJGtUvZ/4bRS+4dbAqPOziGY1GyM6dsLalM0xR9paZVTZSvNSYK1cK0eagzdgUjFbVHwhaJ0pdjIYCQ==", "dependencies": { "tslib": "^2.6.3" }, @@ -1995,11 +1982,11 @@ } }, "node_modules/@loopback/http-server": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@loopback/http-server/-/http-server-6.0.4.tgz", - "integrity": "sha512-e5QyUeX4ime9mbnPOO8la7AccRCJ9Iyw/+HSRDoFV0ufOCZFwVkzu8NLM/NJPyFTPcMe+jTddvgH0+KcmBYCwA==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@loopback/http-server/-/http-server-6.0.6.tgz", + "integrity": "sha512-3GhrLWHi87VVL2jGSpFocfsKYGjBIjoJ8r017/kuMi4UGweNrbSBrL8ly5tKQ6APGjx+/j9qEuFC1IaaXo4FCw==", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.3.7", "stoppable": "^1.1.0", "tslib": "^2.6.3" }, @@ -2008,9 +1995,9 @@ } }, "node_modules/@loopback/logging": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/@loopback/logging/-/logging-0.12.4.tgz", - "integrity": "sha512-+30PxdR2F8V9mrUxTD7eHjhV46LgAyVbZfv4+C9Sjrpa81+e2V/uXBai8o6Ir1pJsQo6fm97EohLttYV3LrNUA==", + "version": "0.12.6", + "resolved": "https://registry.npmjs.org/@loopback/logging/-/logging-0.12.6.tgz", + "integrity": "sha512-v6HpSCVCOzpq7gS9bx6bO42oMa4kKD9NhXfZ1nUJAr6M2ErSzwxdQI1m67CW5DYr8OoziQfO9GSFcqpbXsPI7w==", "dev": true, "dependencies": { "fluent-logger": "^3.4.1", @@ -2028,11 +2015,11 @@ } }, "node_modules/@loopback/metadata": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@loopback/metadata/-/metadata-7.0.4.tgz", - "integrity": "sha512-PMR9FYnWbx9FcnF/oy5ukY852l2xl7eUU1WMOVeiYU66vpXdCXb1bOoTZjMOAuqZcSlQUjqwk15cZzydU/ITxw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@loopback/metadata/-/metadata-7.0.6.tgz", + "integrity": "sha512-NlSx6VcGC97qFiC4yR+3UcT7n7FpEGpyqPwYmbLPiIyKsbv/s5TwCUy/1QprJAx9ZFrM/mkd0nW2z01Ek6PnUw==", "dependencies": { - "debug": "^4.3.5", + "debug": "^4.3.7", "lodash": "^4.17.21", "reflect-metadata": "^0.2.1", "tslib": "^2.6.3" @@ -2042,9 +2029,9 @@ } }, "node_modules/@loopback/model-api-builder": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@loopback/model-api-builder/-/model-api-builder-6.0.4.tgz", - "integrity": "sha512-Or3vS/q76FPZ3voLkvLTP0j3kewa+gM4A3sf0wzGVFEe2/mq2Kkvx2e5hMe/siKEe9VfythQ2SSvf4uJz0zFhg==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/@loopback/model-api-builder/-/model-api-builder-6.0.6.tgz", + "integrity": "sha512-KqluBGENq/VRhgFriyLFPzMAtCEmUbgZ3PUpiFWVzNRLmGccQwl+zfPKNwhl8Q8A5yXNS3y7ANzxcKo/1i2chA==", "dependencies": { "tslib": "^2.6.3" }, @@ -2057,12 +2044,12 @@ } }, "node_modules/@loopback/openapi-v3": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@loopback/openapi-v3/-/openapi-v3-10.0.4.tgz", - "integrity": "sha512-ZZSKd7pMNDP7BgvHcWUeAV3DqTA9g89C6Ra4wCmCHnpvQdxoqoNWI10wupRFKFgbLr/x9YK/Pf9CnwN+WZQIMQ==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@loopback/openapi-v3/-/openapi-v3-10.0.6.tgz", + "integrity": "sha512-pOmYzuv58t5eA0gZKuAeT6GcTBV0+n25OSUjx5CpwhXnq9hfFku7USWUfJLgwEuE75XsTQ7dUUmmBYvpaFCw2Q==", "dependencies": { - "@loopback/repository-json-schema": "^8.0.4", - "debug": "^4.3.5", + "@loopback/repository-json-schema": "^8.0.6", + "debug": "^4.3.7", "http-status": "^1.7.4", "json-merge-patch": "^1.0.2", "lodash": "^4.17.21", @@ -2077,13 +2064,13 @@ } }, "node_modules/@loopback/repository": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@loopback/repository/-/repository-7.0.4.tgz", - "integrity": "sha512-SP9i/38N/S3p8eBhrWSv2or+hRXFkZ6o5bFFr2QvuZtMEFP6OUR1bTUfSlElWGddOAUOpwh7COnn5evezwRhdQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@loopback/repository/-/repository-7.0.6.tgz", + "integrity": "sha512-yxImM2VqC4sD7e++y4KTP9Clp6SymRS2EgTYCUMZ+k+dQ3LqIjQwg6Ro5g0VuQpJMNBr3d0qDW16rPRV9o5VFw==", "dependencies": { - "@loopback/filter": "^5.0.4", + "@loopback/filter": "^5.0.6", "@types/debug": "^4.1.12", - "debug": "^4.3.5", + "debug": "^4.3.7", "lodash": "^4.17.21", "loopback-datasource-juggler": "^5.0.11", "tslib": "^2.6.3" @@ -2096,12 +2083,12 @@ } }, "node_modules/@loopback/repository-json-schema": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@loopback/repository-json-schema/-/repository-json-schema-8.0.4.tgz", - "integrity": "sha512-WUWEQIw66rsw6EHVcsel7Bty/MV93E0HUvn8dcSHexKhoGv/9oae1EYUsaThkjb4hnG2iYZt4ZNDL4SyMJ6aXg==", + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@loopback/repository-json-schema/-/repository-json-schema-8.0.6.tgz", + "integrity": "sha512-oYVURgSIHLhzwuL3J4CwokDQ4Pki1OKnGZerR/Z3sZhcBdP/CwJk1AwZ3XuxTBHS2ebfW7H1Kg9EoOoHRrojtA==", "dependencies": { "@types/json-schema": "^7.0.15", - "debug": "^4.3.5", + "debug": "^4.3.7", "tslib": "^2.6.3" }, "engines": { @@ -2113,13 +2100,13 @@ } }, "node_modules/@loopback/rest": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@loopback/rest/-/rest-14.0.4.tgz", - "integrity": "sha512-1oQBJ1J+i7i4NsA9WzhGZJ2R9CK/aK2VTbQb6wlIgeSg5t65uSqUYUYgcY/Gt1wOJudZQ6l0CQJKhK1sdZj7+A==", + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/@loopback/rest/-/rest-14.0.6.tgz", + "integrity": "sha512-/YPSwhzCNnvkkc3kMhATkg2CeMy4rklGd9dTvXHNLJgZ/KplvwKRlI+IKAKYeUcnc5m51eDD3YcUBjlAlg9wfQ==", "dependencies": { - "@loopback/express": "^7.0.4", - "@loopback/http-server": "^6.0.4", - "@loopback/openapi-v3": "^10.0.4", + "@loopback/express": "^7.0.6", + "@loopback/http-server": "^6.0.6", + "@loopback/openapi-v3": "^10.0.6", "@openapi-contrib/openapi-schema-to-json-schema": "^5.1.0", "@types/body-parser": "^1.19.5", "@types/cors": "^2.8.17", @@ -2133,9 +2120,9 @@ "ajv-errors": "^3.0.0", "ajv-formats": "^3.0.1", "ajv-keywords": "^5.1.0", - "body-parser": "^1.20.2", + "body-parser": "^1.20.3", "cors": "^2.8.5", - "debug": "^4.3.5", + "debug": "^4.3.7", "express": "^4.19.2", "http-errors": "^2.0.0", "js-yaml": "^4.1.0", @@ -2143,7 +2130,7 @@ "lodash": "^4.17.21", "on-finished": "^2.4.1", "path-to-regexp": "^6.2.2", - "qs": "^6.12.2", + "qs": "^6.12.3", "strong-error-handler": "^5.0.10", "tslib": "^2.6.3", "type-is": "^1.6.18", @@ -2157,9 +2144,9 @@ } }, "node_modules/@loopback/rest-explorer": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@loopback/rest-explorer/-/rest-explorer-7.0.4.tgz", - "integrity": "sha512-qz9gdjVIQ7YzH48iDBaL/lW4B40faR+Peg6AnJE0yetLG6DBi2Sm4yjzSFU7IGz4PbPG5wt1lDxdrutWK632lA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@loopback/rest-explorer/-/rest-explorer-7.0.6.tgz", + "integrity": "sha512-YU8bRw+oNIXubFoIgZWVwGXWweSl+N+vyvVmcv6c+pcT8aaLc+HWT1OAiRiVIl1U6EzdAvC6igBpKUsFbjHwtA==", "dependencies": { "ejs": "^3.1.10", "swagger-ui-dist": "5.17.14", @@ -2213,9 +2200,9 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/@loopback/service-proxy": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@loopback/service-proxy/-/service-proxy-7.0.4.tgz", - "integrity": "sha512-Nw1S71eqNszN874AwmoRLQcT7aocD7E6aomB0Nff5jTTDONkZpIE+Zancyn0EtVp58DTVa2K+x9sPfiaL1XwYQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@loopback/service-proxy/-/service-proxy-7.0.6.tgz", + "integrity": "sha512-MS57pxCMQl9G5w22Hf2bGGjTaaA+JwjdloOjrqo5oroJXtjgMaGbZZ+h7FM59WQb3LP9qTlUCg4xtXrU04uMlw==", "dependencies": { "loopback-datasource-juggler": "^5.0.11", "tslib": "^2.6.3" @@ -2228,9 +2215,9 @@ } }, "node_modules/@loopback/testlab": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@loopback/testlab/-/testlab-7.0.4.tgz", - "integrity": "sha512-8ZuiyMpdSuIHyA3Hxe+XyH9vEEndKTr093uPsSwevmks+oInCwMB9Y7Op5t2Ef9SEU+p8iSpfW8ikf2cp8VJ0g==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@loopback/testlab/-/testlab-7.0.6.tgz", + "integrity": "sha512-pRYqTKB7HpDRJrM1pHapMSnBDWWzD9cs2BFSXcYcq061xFGYjqyZuMi7BWT1vz4iVGQ0zKt+qyqmCVWONmqalw==", "dev": true, "dependencies": { "@hapi/shot": "^6.0.1", @@ -3006,18 +2993,13 @@ } }, "node_modules/@openapi-contrib/openapi-schema-to-json-schema/node_modules/@types/node": { - "version": "20.14.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.14.tgz", - "integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==", + "version": "20.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", + "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, - "node_modules/@openapi-contrib/openapi-schema-to-json-schema/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, "node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -3028,9 +3010,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz", - "integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.26.0.tgz", + "integrity": "sha512-HedpXXYzzbaoutw6DFLWLDket2FwLkLpil4hGCZ1xYEIMTcivdfwEOISgdbLEWyG3HW52gTq2V9mOVJrONgiwg==", "engines": { "node": ">=14" }, @@ -3039,11 +3021,11 @@ } }, "node_modules/@opentelemetry/core": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz", - "integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.26.0.tgz", + "integrity": "sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ==", "dependencies": { - "@opentelemetry/semantic-conventions": "1.25.1" + "@opentelemetry/semantic-conventions": "1.27.0" }, "engines": { "node": ">=14" @@ -3053,13 +3035,13 @@ } }, "node_modules/@opentelemetry/exporter-jaeger": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.25.1.tgz", - "integrity": "sha512-6/HwzrwUx0fpkFXrouF0IJp+hpN8xkx8RqEk+BZfeoMAHydpyigyYsKyAtAZRwfJe45WWJbJUqoK8aBjiC9iLQ==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-jaeger/-/exporter-jaeger-1.26.0.tgz", + "integrity": "sha512-l5NMFwFr5NWWRNcURUS8/RdkBmR3+dPGE33f51XfamKXsEfZUkRC8V1L2D7hzKhXxcFmYLcprg4/sYpeKtYoAQ==", "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/sdk-trace-base": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1", + "@opentelemetry/core": "1.26.0", + "@opentelemetry/sdk-trace-base": "1.26.0", + "@opentelemetry/semantic-conventions": "1.27.0", "jaeger-client": "^3.15.0" }, "engines": { @@ -3262,11 +3244,11 @@ } }, "node_modules/@opentelemetry/propagator-b3": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz", - "integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.26.0.tgz", + "integrity": "sha512-vvVkQLQ/lGGyEy9GT8uFnI047pajSOVnZI2poJqVGD3nJ+B9sFGdlHNnQKophE3lHfnIH0pw2ubrCTjZCgIj+Q==", "dependencies": { - "@opentelemetry/core": "1.25.1" + "@opentelemetry/core": "1.26.0" }, "engines": { "node": ">=14" @@ -3276,11 +3258,11 @@ } }, "node_modules/@opentelemetry/propagator-jaeger": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz", - "integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.26.0.tgz", + "integrity": "sha512-DelFGkCdaxA1C/QA0Xilszfr0t4YbGd3DjxiCDPh34lfnFr+VkkrjV9S8ZTJvAzfdKERXhfOxIKBoGPJwoSz7Q==", "dependencies": { - "@opentelemetry/core": "1.25.1" + "@opentelemetry/core": "1.26.0" }, "engines": { "node": ">=14" @@ -3290,12 +3272,12 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz", - "integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.26.0.tgz", + "integrity": "sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw==", "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" + "@opentelemetry/core": "1.26.0", + "@opentelemetry/semantic-conventions": "1.27.0" }, "engines": { "node": ">=14" @@ -3305,13 +3287,13 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz", - "integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz", + "integrity": "sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw==", "dependencies": { - "@opentelemetry/core": "1.25.1", - "@opentelemetry/resources": "1.25.1", - "@opentelemetry/semantic-conventions": "1.25.1" + "@opentelemetry/core": "1.26.0", + "@opentelemetry/resources": "1.26.0", + "@opentelemetry/semantic-conventions": "1.27.0" }, "engines": { "node": ">=14" @@ -3321,15 +3303,15 @@ } }, "node_modules/@opentelemetry/sdk-trace-node": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz", - "integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==", - "dependencies": { - "@opentelemetry/context-async-hooks": "1.25.1", - "@opentelemetry/core": "1.25.1", - "@opentelemetry/propagator-b3": "1.25.1", - "@opentelemetry/propagator-jaeger": "1.25.1", - "@opentelemetry/sdk-trace-base": "1.25.1", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.26.0.tgz", + "integrity": "sha512-Fj5IVKrj0yeUwlewCRwzOVcr5avTuNnMHWf7GPc1t6WaT78J6CJyF3saZ/0RkZfdeNO8IcBl/bNcWMVZBMRW8Q==", + "dependencies": { + "@opentelemetry/context-async-hooks": "1.26.0", + "@opentelemetry/core": "1.26.0", + "@opentelemetry/propagator-b3": "1.26.0", + "@opentelemetry/propagator-jaeger": "1.26.0", + "@opentelemetry/sdk-trace-base": "1.26.0", "semver": "^7.5.2" }, "engines": { @@ -3351,9 +3333,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", - "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz", + "integrity": "sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg==", "engines": { "node": ">=14" } @@ -3652,29 +3634,29 @@ } }, "node_modules/@sinonjs/samsam": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", - "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0", + "@sinonjs/commons": "^3.0.1", "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "type-detect": "^4.1.0" } }, - "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, - "dependencies": { - "type-detect": "4.0.8" + "engines": { + "node": ">=4" } }, "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==", "dev": true }, "node_modules/@sourceloop/core": { @@ -3831,9 +3813,9 @@ } }, "node_modules/@types/aws-lambda": { - "version": "8.10.142", - "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.142.tgz", - "integrity": "sha512-wy2y/2hQKrS6myOS++koXg3N1Hg+LLyPjaggCFajczSHZPqBnOMuT2sdH3kiASrmdBYyM3pmjyz5SoWraRllCQ==", + "version": "8.10.145", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.145.tgz", + "integrity": "sha512-dtByW6WiFk5W5Jfgz1VM+YPA21xMXTuSFoLYIDY0L44jDLLflVPtZkYuu3/YxpGcvjzKFBZLU+GyKjR0HOYtyw==", "dev": true }, "node_modules/@types/body-parser": { @@ -3942,9 +3924,9 @@ } }, "node_modules/@types/jsonwebtoken": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz", - "integrity": "sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", + "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", "dependencies": { "@types/node": "*" } @@ -3977,9 +3959,9 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.7.tgz", - "integrity": "sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw==", + "version": "10.0.8", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.8.tgz", + "integrity": "sha512-HfMcUmy9hTMJh66VNcmeC9iVErIZJli2bszuXc6julh5YGuRb/W5OnkHjwLNYdFlMis0sY3If5SEAp+PktdJjw==", "dev": true }, "node_modules/@types/moment": { @@ -3998,11 +3980,11 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", - "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", + "version": "22.5.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", + "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", "dependencies": { - "undici-types": "~6.13.0" + "undici-types": "~6.19.2" } }, "node_modules/@types/normalize-package-data": { @@ -4026,18 +4008,18 @@ "dev": true }, "node_modules/@types/pdfkit": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.13.4.tgz", - "integrity": "sha512-ixGNDHYJCCKuamY305wbfYSphZ2WPe8FPkjn8oF4fHV+PgPV4V+hecPh2VOS2h4RNtpSB3zQcR4sCpNvvrEb1A==", + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.13.5.tgz", + "integrity": "sha512-cR4gZA3xiMVDUf/O/ijVr6aIguvN72ZmCDLcWwM0ycqn5P8XDSjMX9PixzWEoDnlINoofxo2LCdV4KvdD9Waqg==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/pg": { - "version": "8.11.6", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz", - "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==", + "version": "8.11.10", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.10.tgz", + "integrity": "sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==", "dependencies": { "@types/node": "*", "pg-protocol": "*", @@ -4050,9 +4032,9 @@ "integrity": "sha512-uALowNG2TSM1HNPMMOR0AJwv4aPYPhqB0xlEhkeRTMuto5hjoSPZkvgu1nbPUkz3gEPAHv4sy4DmKsurZiEfRQ==" }, "node_modules/@types/qs": { - "version": "6.9.15", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", - "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + "version": "6.9.16", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", + "integrity": "sha512-7i+zxXdPD0T4cKDuxCUXJ4wHcsJLwENa6Z3dCu8cfCK743OGy5Nu1RmAGqDPsoTDINVEcdXKRvR/zre+P2Ku1A==" }, "node_modules/@types/range-parser": { "version": "1.2.7", @@ -4103,9 +4085,9 @@ "dev": true }, "node_modules/@types/superagent": { - "version": "8.1.8", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.8.tgz", - "integrity": "sha512-nTqHJ2OTa7PFEpLahzSEEeFeqbMpmcN7OeayiOc7v+xk+/vyTKljRe+o4MPqSnPeRCMvtxuLG+5QqluUVQJOnA==", + "version": "8.1.9", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", + "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", "dev": true, "dependencies": { "@types/cookiejar": "^2.1.5", @@ -4417,12 +4399,11 @@ "dev": true }, "node_modules/accept-language": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/accept-language/-/accept-language-3.0.18.tgz", - "integrity": "sha512-sUofgqBPzgfcF20sPoBYGQ1IhQLt2LSkxTnlQSuLF3n5gPEqd5AimbvOvHEi0T1kLMiGVqPWzI5a9OteBRth3A==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/accept-language/-/accept-language-3.0.20.tgz", + "integrity": "sha512-xklPzRma4aoDEPk0ZfMjeuxB2FP4JBYlAR25OFUqCoOYDjYo6wGwAs49SnTN/MoB5VpnNX9tENfZ+vEIFmHQMQ==", "dependencies": { - "bcp47": "^1.1.2", - "stable": "^0.1.6" + "bcp47": "^1.1.2" } }, "node_modules/accepts": { @@ -4459,9 +4440,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", - "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "dependencies": { "acorn": "^8.11.0" @@ -4789,9 +4770,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, "node_modules/asynckit": { "version": "0.4.0", @@ -4844,15 +4825,15 @@ } }, "node_modules/aws4": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.0.tgz", - "integrity": "sha512-3AungXC4I8kKsS9PuS4JH2nc+0bVY/mjgrephHTIi8fpEeGsTHBUJeosp0Wc1myYMElmD0B3Oc4XL/HVJ4PV2g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "peer": true }, "node_modules/axios": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", - "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -4957,11 +4938,6 @@ "safe-buffer": "^5.1.1" } }, - "node_modules/bl/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, "node_modules/bl/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -4995,9 +4971,9 @@ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -5007,7 +4983,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -5030,20 +5006,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -5478,9 +5440,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001649", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001649.tgz", - "integrity": "sha512-fJegqZZ0ZX8HOWr6rcafGr72+xcgJKI9oWfDW5DrD7ExUtgZC7a7R7ZYmZqplh7XDocFdGeIFn7roAxhOeYrPQ==", + "version": "1.0.30001662", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz", + "integrity": "sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==", "dev": true, "funding": [ { @@ -5620,6 +5582,18 @@ "node": "*" } }, + "node_modules/chargebee": { + "version": "2.42.0", + "resolved": "https://registry.npmjs.org/chargebee/-/chargebee-2.42.0.tgz", + "integrity": "sha512-kqWkGnPmUFDn+i2QkI9W3//SV2uihIGu9t0xE9QGXpsvnTj9V9BX5SBbUa4cWv/Kl4wMRNB1I94bYYwD6pe6KA==", + "dependencies": { + "q": ">=1.0.1", + "safer-buffer": "2.1.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -6985,11 +6959,11 @@ } }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -7071,6 +7045,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deep-equal/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -7409,9 +7388,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz", - "integrity": "sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==", + "version": "1.5.26", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.26.tgz", + "integrity": "sha512-Z+OMe9M/V6Ep9n/52+b7lkvYEps26z4Yz3vjWL1V61W0q+VLF1pOHhMY17sa4roz4AWmULSI8E6SAojZA5L0YQ==", "dev": true }, "node_modules/emoji-regex": { @@ -7425,9 +7404,9 @@ "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "engines": { "node": ">= 0.8" } @@ -7617,6 +7596,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-get-iterator/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -7664,9 +7648,9 @@ "dev": true }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -7689,16 +7673,16 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -7999,36 +7983,36 @@ "dev": true }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -8064,23 +8048,9 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "node_modules/express/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" }, "node_modules/expression-eval": { "version": "5.0.1", @@ -8289,12 +8259,12 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -8524,9 +8494,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -8567,9 +8537,9 @@ } }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -8867,12 +8837,6 @@ "wrap-ansi": "^7.0.0" } }, - "node_modules/get-pkg-repo/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/get-pkg-repo/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -9598,9 +9562,9 @@ } }, "node_modules/hyperid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-3.2.0.tgz", - "integrity": "sha512-PdTtDo+Rmza9nEhTunaDSUKwbC69TIzLEpZUwiB6f+0oqmY0UPfhyHCPt6K1NQ4WFv5yJBTG5vELztVWP+nEVQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-3.3.0.tgz", + "integrity": "sha512-7qhCVT4MJIoEsNcbhglhdmBKb09QtcmJNiIQGq7js/Khf5FtQQ9bzcAuloeqBeee7XD7JqDeve9KNlQya5tSGQ==", "dependencies": { "buffer": "^5.2.1", "uuid": "^8.3.2", @@ -9665,9 +9629,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -10079,9 +10043,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dependencies": { "hasown": "^2.0.2" }, @@ -10463,9 +10427,9 @@ } }, "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" }, "node_modules/isexe": { "version": "2.0.0", @@ -11827,9 +11791,9 @@ } }, "node_modules/loopback-connector-postgresql": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/loopback-connector-postgresql/-/loopback-connector-postgresql-7.1.3.tgz", - "integrity": "sha512-MaRNOLbjEDz3VGqP32CGI0DdtvJc/f2OOCYeXkizPCFhFElOxL81qPlN4h9RMVoFuLF7AYdxgGQhev6sOv2psw==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/loopback-connector-postgresql/-/loopback-connector-postgresql-7.1.5.tgz", + "integrity": "sha512-WKKd5rXSEu5GorEUJjIx/Q35qjHhidlu2e/hwudu7ha4k2zGeKnFBbE2fS5RUB5iC/2u+dG23VPl4B4s/KdNKg==", "dependencies": { "async": "^3.2.0", "bluebird": "^3.4.6", @@ -11845,13 +11809,13 @@ } }, "node_modules/loopback-connector-postgresql/node_modules/loopback-connector": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/loopback-connector/-/loopback-connector-6.1.7.tgz", - "integrity": "sha512-Mc5EkBjOfiCu2d+xlS094oIT8BaT50IohJeOCGB55Gj8iM6EWZOthhEtID0IUHq+7v+/okkf6OhvqILybIIyMw==", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/loopback-connector/-/loopback-connector-6.1.9.tgz", + "integrity": "sha512-Ii4CAHnY4ChKj/2bsiV2Ck59QWcYHTE2HbU3028D2nFWg7N5SAdkkjnhdvqUhzvdt+Vpb0WlEgaR4SKASHqXEw==", "dependencies": { - "async": "^3.2.5", + "async": "^3.2.6", "bluebird": "^3.7.2", - "debug": "^4.3.5", + "debug": "^4.3.7", "msgpack5": "^4.5.1", "strong-globalize": "^6.0.6", "uuid": "^10.0.0" @@ -11907,20 +11871,20 @@ } }, "node_modules/loopback-datasource-juggler": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/loopback-datasource-juggler/-/loopback-datasource-juggler-5.0.11.tgz", - "integrity": "sha512-qPgJA4VbrsclgesjPMkuzCpa+emuVTrmgY89zO7zmWvLnBzCPkIISxhoCAIo6Z3n6VSXkgzNKcstj7lwgbqXhw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/loopback-datasource-juggler/-/loopback-datasource-juggler-5.1.0.tgz", + "integrity": "sha512-oHQ6S4r8/65tR5JfjjDke7Z9QdbBXO21/iy2LqkmvHTGPTa3A61r8mwTOGQtDHpVRJijt+eDHbrufVLzITZEsw==", "dependencies": { - "async": "^3.2.5", + "async": "^3.2.6", "change-case": "^4.1.2", - "debug": "^4.3.5", + "debug": "^4.3.7", "depd": "^2.0.0", "inflection": "^3.0.0", "lodash": "^4.17.21", - "loopback-connector": "^6.1.7", - "minimatch": "^9.0.5", + "loopback-connector": "^6.1.9", + "minimatch": "^10.0.1", "nanoid": "^3.3.7", - "qs": "^6.12.2", + "qs": "^6.13.0", "strong-globalize": "^6.0.6", "traverse": "^0.6.9", "uuid": "^10.0.0" @@ -11938,13 +11902,13 @@ } }, "node_modules/loopback-datasource-juggler/node_modules/loopback-connector": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/loopback-connector/-/loopback-connector-6.1.7.tgz", - "integrity": "sha512-Mc5EkBjOfiCu2d+xlS094oIT8BaT50IohJeOCGB55Gj8iM6EWZOthhEtID0IUHq+7v+/okkf6OhvqILybIIyMw==", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/loopback-connector/-/loopback-connector-6.1.9.tgz", + "integrity": "sha512-Ii4CAHnY4ChKj/2bsiV2Ck59QWcYHTE2HbU3028D2nFWg7N5SAdkkjnhdvqUhzvdt+Vpb0WlEgaR4SKASHqXEw==", "dependencies": { - "async": "^3.2.5", + "async": "^3.2.6", "bluebird": "^3.7.2", - "debug": "^4.3.5", + "debug": "^4.3.7", "msgpack5": "^4.5.1", "strong-globalize": "^6.0.6", "uuid": "^10.0.0" @@ -11953,6 +11917,20 @@ "node": ">=18" } }, + "node_modules/loopback-datasource-juggler/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/loopback4-authentication": { "version": "12.0.2", "resolved": "https://registry.npmjs.org/loopback4-authentication/-/loopback4-authentication-12.0.2.tgz", @@ -12010,6 +11988,25 @@ "node": ">=18" } }, + "node_modules/loopback4-billing": { + "version": "0.0.1", + "resolved": "file:services/subscription-service/loopback4-billing-0.0.1.tgz", + "integrity": "sha512-jQzEnJ/QcDwBKV2kcAzRKlrhfGZdTsMBOykmuLLk2649BfNnEQsP4Q+zy+Ceg92cYSD/wnj6dK31EeFG2DtxXQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@loopback/rest": "^14.0.0", + "@loopback/rest-explorer": "^7.0.0", + "chargebee": "^2.38.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@loopback/core": "^6.0.0" + } + }, "node_modules/loopback4-dynamic-datasource": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/loopback4-dynamic-datasource/-/loopback4-dynamic-datasource-2.0.1.tgz", @@ -12452,9 +12449,12 @@ "dev": true }, "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -12479,9 +12479,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { "braces": "^3.0.3", @@ -12766,9 +12766,9 @@ } }, "node_modules/mocha": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", - "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", "dev": true, "dependencies": { "ansi-colors": "^4.1.3", @@ -12843,12 +12843,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -13082,9 +13076,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/msgpack-lite": { "version": "0.1.26", @@ -13101,12 +13095,6 @@ "msgpack": "bin/msgpack" } }, - "node_modules/msgpack-lite/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/msgpack5": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/msgpack5/-/msgpack5-4.5.1.tgz", @@ -13118,11 +13106,6 @@ "safe-buffer": "^5.1.2" } }, - "node_modules/msgpack5/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, "node_modules/msgpack5/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -13264,16 +13247,34 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/nise": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-6.0.0.tgz", - "integrity": "sha512-K8ePqo9BFvN31HXwEtTNGzgrPpmvgciDsFz8aztFjt4LqKO/JeFD8tBOeuDiCMXrIl/m1YvfH8auSpxfaD09wg==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", + "integrity": "sha512-aMSAzLVY7LyeM60gvBS423nBmIPP+Wy7St7hsb+8/fc1HmeoHJfLO8CKse4u3BtOZvQLJghYPI2i/1WZrEj5/g==", "dev": true, "dependencies": { - "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^11.2.2", - "@sinonjs/text-encoding": "^0.7.2", + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.1", + "@sinonjs/text-encoding": "^0.7.3", "just-extend": "^6.2.0", - "path-to-regexp": "^6.2.1" + "path-to-regexp": "^8.1.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.2.tgz", + "integrity": "sha512-4Bb+oqXZTSTZ1q27Izly9lv8B9dlV61CROxPiVtywwzv5SnytJqhvYe6FclHYuXml4cd1VHPo1zd5PmTeJozvA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/nise/node_modules/path-to-regexp": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.1.0.tgz", + "integrity": "sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ==", + "dev": true, + "engines": { + "node": ">=16" } }, "node_modules/no-case": { @@ -13366,9 +13367,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", "dev": true, "bin": { "node-gyp-build": "bin.js", @@ -15604,9 +15605,9 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/path-to-regexp": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==" }, "node_modules/path-type": { "version": "4.0.0", @@ -15641,13 +15642,13 @@ "peer": true }, "node_modules/pg": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", - "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz", + "integrity": "sha512-34wkUTh3SxTClfoHB3pQ7bIMvw9dpFU1audQQeZG837fmHfHpr14n/AELVDoOYVDW2h5RDWU78tFjkD+erSBsw==", "dependencies": { - "pg-connection-string": "^2.6.4", - "pg-pool": "^3.6.2", - "pg-protocol": "^1.6.1", + "pg-connection-string": "^2.7.0", + "pg-pool": "^3.7.0", + "pg-protocol": "^1.7.0", "pg-types": "^2.1.0", "pgpass": "1.x" }, @@ -15673,9 +15674,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", - "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", + "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==" }, "node_modules/pg-int8": { "version": "1.0.1", @@ -15694,17 +15695,17 @@ } }, "node_modules/pg-pool": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", - "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.0.tgz", + "integrity": "sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", - "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.0.tgz", + "integrity": "sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==" }, "node_modules/pg-types": { "version": "4.0.2", @@ -15790,9 +15791,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, "node_modules/picomatch": { @@ -15937,9 +15938,9 @@ "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==" }, "node_modules/postman-request": { - "version": "2.88.1-postman.37", - "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.37.tgz", - "integrity": "sha512-TpHeMnvO5xvlYCYp8QntLR1Fq0hohWGOLbf9RBqO5JTMdPWZpGBbR8xs11tHsZRVMDXWFg4m960ItkcDxiaWSA==", + "version": "2.88.1-postman.40", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.40.tgz", + "integrity": "sha512-uE4AiIqhjtHKp4pj9ei7fkdfNXEX9IqDBlK1plGAQne6y79UUlrTdtYLhwXoO0AMOvqyl9Ar+BU6Eo6P/MPgfg==", "peer": true, "dependencies": { "@postman/form-data": "~3.1.1", @@ -16198,9 +16199,9 @@ "dev": true }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -17018,6 +17019,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -17059,9 +17065,9 @@ } }, "node_modules/safe-stable-stringify": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", - "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "engines": { "node": ">=10" } @@ -17098,9 +17104,9 @@ } }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -17133,6 +17139,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/send/node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -17144,11 +17158,6 @@ "node": ">=4" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, "node_modules/sentence-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", @@ -17169,14 +17178,14 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -17547,13 +17556,13 @@ } }, "node_modules/sinon": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-18.0.0.tgz", - "integrity": "sha512-+dXDXzD1sBO6HlmZDd7mXZCR/y5ECiEiGCBSGuFD/kZ0bDTofPYc6JaeGmPSF+1j1MejGUWkORbYOLDyvqCWpA==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-18.0.1.tgz", + "integrity": "sha512-a2N2TDY1uGviajJ6r4D1CyRAkzE9NNVlYOV1wX5xQDuAk0ONgzgRl0EjCQuRCPxOwp13ghsMwt9Gdldujs39qw==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/fake-timers": "11.2.2", "@sinonjs/samsam": "^8.0.0", "diff": "^5.2.0", "nise": "^6.0.0", @@ -17805,9 +17814,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, "node_modules/split": { @@ -17910,12 +17919,6 @@ "node": ">=8" } }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -18123,12 +18126,12 @@ } }, "node_modules/strong-error-handler": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/strong-error-handler/-/strong-error-handler-5.0.10.tgz", - "integrity": "sha512-bSjeWSHizlsefxweVeFv2Pha7z78XL1EVmuMSBkypjBJyYgHgvrqFgjwmKiCBTYt9C15xNnJjUfkRVkuSZTyNw==", + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/strong-error-handler/-/strong-error-handler-5.0.11.tgz", + "integrity": "sha512-OVvvpTHg3BmNWBsg3St33ADHqBPSRAsAVlwIUyBFNib0fXuY5dCjKyuJAiT2LW9Y1DB182xRFkt6IblcwTbBbw==", "dependencies": { "accepts": "^1.3.8", - "debug": "^4.3.4", + "debug": "^4.3.7", "fast-safe-stringify": "^2.1.1", "handlebars": "^4.7.8", "http-status": "^1.7.4", @@ -18259,6 +18262,61 @@ "prom-client": ">= 10 <= 14" } }, + "node_modules/swagger-stats/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/swagger-stats/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/swagger-stats/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/swagger-stats/node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/swagger-stats/node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/swagger-stats/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -18642,9 +18700,9 @@ "dev": true }, "node_modules/traverse": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.9.tgz", - "integrity": "sha512-7bBrcF+/LQzSgFmT0X5YclVqQxtv7TDJ1f8Wj7ibBu/U6BMLeOpUxuZjV7rMc44UtKxlnMFigdhFAIszSX1DMg==", + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.10.tgz", + "integrity": "sha512-hN4uFRxbK+PX56DxYiGHsTn2dME3TVr9vbNqlQGcGcPhJAn+tdP126iA+TArMpI4YSgnTkMWyoLl5bf81Hi5TA==", "dependencies": { "gopd": "^1.0.1", "typedarray.prototype.slice": "^1.0.3", @@ -18771,9 +18829,9 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/tsscmp": { "version": "1.0.6", @@ -19148,9 +19206,9 @@ } }, "node_modules/uglify-js": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.1.tgz", - "integrity": "sha512-y/2wiW+ceTYR2TSSptAhfnEtpLaQ4Ups5zrjB2d3kuVxHj16j/QJwPl5PvuGy9uARb39J0+iKxcRPvtpsx4A4A==", + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "optional": true, "bin": { "uglifyjs": "bin/uglifyjs" @@ -19201,9 +19259,9 @@ } }, "node_modules/undici-types": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", - "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==" + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/unicode-properties": { "version": "1.4.1", @@ -19561,9 +19619,9 @@ } }, "node_modules/winston": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.13.1.tgz", - "integrity": "sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.14.2.tgz", + "integrity": "sha512-CO8cdpBB2yqzEf8v895L+GNKYJiEq8eKlHU38af3snQBQ+sdAIUepjMSguOIJC7ICbzm0ZI+Af2If4vIJrtmOg==", "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", @@ -20028,108 +20086,19 @@ } }, "services/orchestrator-service/node_modules/@types/node": { - "version": "18.19.43", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.43.tgz", - "integrity": "sha512-Mw/YlgXnyJdEwLoFv2dpuJaDFriX+Pc+0qOBJ57jC1H6cDxIj2xc5yUrdtArDVG0m+KV6622a4p2tenEqB3C/g==", + "version": "18.19.50", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz", + "integrity": "sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, - "services/orchestrator-service/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "services/orchestrator-service/node_modules/has-flag": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "services/orchestrator-service/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "services/orchestrator-service/node_modules/nodemon": { - "version": "3.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "services/orchestrator-service/node_modules/semver": { - "version": "7.6.3", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "services/orchestrator-service/node_modules/simple-update-notifier": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "services/orchestrator-service/node_modules/supports-color": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "services/orchestrator-service/node_modules/typescript": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20153,7 +20122,7 @@ "@loopback/context": "^7.0.2", "@loopback/core": "^6.0.2", "@loopback/openapi-v3": "^10.0.2", - "@loopback/repository": "^7.0.2", + "@loopback/repository": "^7.0.4", "@loopback/rest": "^14.0.2", "@loopback/rest-explorer": "^7.0.2", "@loopback/service-proxy": "^7.0.2", @@ -20173,6 +20142,7 @@ "loopback-connector-postgresql": "^7.1.1", "loopback4-authentication": "^12.0.2", "loopback4-authorization": "^7.0.2", + "loopback4-billing": "file:loopback4-billing-0.0.1.tgz", "swagger-stats": "^0.99.5", "symlink-resolver": "0.2.1", "tslib": "^2.6.2" @@ -20195,9 +20165,10 @@ } }, "services/subscription-service/node_modules/@types/node": { - "version": "18.19.31", + "version": "18.19.50", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz", + "integrity": "sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==", "dev": true, - "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } @@ -20278,9 +20249,10 @@ } }, "services/tenant-management-service/node_modules/@types/node": { - "version": "18.19.31", + "version": "18.19.50", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz", + "integrity": "sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==", "dev": true, - "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } diff --git a/services/subscription-service/.gitignore b/services/subscription-service/.gitignore index 162c24c..fc51452 100644 --- a/services/subscription-service/.gitignore +++ b/services/subscription-service/.gitignore @@ -49,7 +49,7 @@ typings/ .node_repl_history # Output of 'npm pack' -*.tgz +# *.tgz # Yarn Integrity file .yarn-integrity diff --git a/services/subscription-service/loopback4-billing-0.0.1.tgz b/services/subscription-service/loopback4-billing-0.0.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..0877da65634cda9b59fdbd8156f52c55dc9756e6 GIT binary patch literal 29337 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 literal 0 HcmV?d00001 diff --git a/services/subscription-service/migrations/pg/migrations/20240209122448-add-customer-table.js b/services/subscription-service/migrations/pg/migrations/20240209122448-add-customer-table.js new file mode 100644 index 0000000..17c1fd9 --- /dev/null +++ b/services/subscription-service/migrations/pg/migrations/20240209122448-add-customer-table.js @@ -0,0 +1,59 @@ +'use strict'; + +let dbm; +let type; +let seed; +let fs = require('fs'); +let path = require('path'); +let Promise; + +/** + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { + dbm = options.dbmigrate; + type = dbm.dataType; + seed = seedLink; + Promise = options.Promise; +}; + +exports.up = function (db) { + let filePath = path.join( + __dirname, + 'sqls', + '20240209122448-add-customer-table-up.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, {encoding: 'utf-8'}, function (err, data) { + if (err) return reject(err); + console.log('received data: ' + data); + + resolve(data); + }); + }).then(function (data) { + return db.runSql(data); + }); +}; + +exports.down = function (db) { + let filePath = path.join( + __dirname, + 'sqls', + '20240209122448-add-customer-table-down.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, {encoding: 'utf-8'}, function (err, data) { + if (err) return reject(err); + console.log('received data: ' + data); + + resolve(data); + }); + }).then(function (data) { + return db.runSql(data); + }); +}; + +exports._meta = { + version: 1, +}; diff --git a/services/subscription-service/migrations/pg/migrations/sqls/20240205055601-init-up.sql b/services/subscription-service/migrations/pg/migrations/sqls/20240205055601-init-up.sql index f5da7f9..4c2261f 100644 --- a/services/subscription-service/migrations/pg/migrations/sqls/20240205055601-init-up.sql +++ b/services/subscription-service/migrations/pg/migrations/sqls/20240205055601-init-up.sql @@ -21,6 +21,8 @@ CREATE TABLE plans ( CONSTRAINT pk_plans_id PRIMARY KEY ( id ) ); + + CREATE TABLE subscriptions ( id uuid DEFAULT (md5(((random())::text || (clock_timestamp())::text)))::uuid NOT NULL , created_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL , diff --git a/services/subscription-service/migrations/pg/migrations/sqls/20240209122448-add-customer-table-down.sql b/services/subscription-service/migrations/pg/migrations/sqls/20240209122448-add-customer-table-down.sql new file mode 100644 index 0000000..66e21ae --- /dev/null +++ b/services/subscription-service/migrations/pg/migrations/sqls/20240209122448-add-customer-table-down.sql @@ -0,0 +1,2 @@ +drop table main.billing_customer; +drop table main.invoice; \ No newline at end of file diff --git a/services/subscription-service/migrations/pg/migrations/sqls/20240209122448-add-customer-table-up.sql b/services/subscription-service/migrations/pg/migrations/sqls/20240209122448-add-customer-table-up.sql new file mode 100644 index 0000000..7cd5d30 --- /dev/null +++ b/services/subscription-service/migrations/pg/migrations/sqls/20240209122448-add-customer-table-up.sql @@ -0,0 +1,41 @@ + +CREATE TABLE main.billing_customer ( + id uuid DEFAULT (md5(((random())::text || (clock_timestamp())::text)))::uuid NOT NULL, + tenant_id varchar(255) NOT NULL, + customer_id varchar(255) NOT NULL, + payment_source_id varchar(255), + created_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL, + modified_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL, + deleted boolean DEFAULT false NOT NULL, + deleted_on timestamptz, + deleted_by uuid, + created_by uuid NOT NULL, + modified_by uuid, + CONSTRAINT pk_billing_customer_id PRIMARY KEY (id), + CONSTRAINT uq_billing_customer_customer_id UNIQUE (customer_id) +); + + + +CREATE TABLE main.invoice ( + id UUID DEFAULT (md5(((random())::text || (clock_timestamp())::text)))::uuid NOT NULL, + invoice_id VARCHAR(255) NOT NULL, + invoice_status VARCHAR(255), + billing_customer_id uuid NOT NULL, + -- subscription_id uuid, + created_on TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, + modified_on TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP NOT NULL, + deleted BOOLEAN DEFAULT false NOT NULL, + deleted_on TIMESTAMPTZ, + deleted_by UUID, + created_by UUID NOT NULL, + modified_by UUID, + CONSTRAINT pk_invoice_id PRIMARY KEY (id), + CONSTRAINT fk_invoice_customer FOREIGN KEY (billing_customer_id) REFERENCES main.billing_customer(id) + + -- CONSTRAINT fk_invoice_subscription FOREIGN KEY (subscription_id) REFERENCES main.subscriptions(id) -- Add this constraint +); + +ALTER TABLE main.subscriptions +ADD COLUMN invoice_id uuid NOT NULL, +ADD CONSTRAINT fk_subscriptions_invoice FOREIGN KEY (invoice_id) REFERENCES main.invoice(id); \ No newline at end of file diff --git a/services/subscription-service/package.json b/services/subscription-service/package.json index a9d1822..4e76816 100644 --- a/services/subscription-service/package.json +++ b/services/subscription-service/package.json @@ -67,7 +67,7 @@ "@loopback/context": "^7.0.2", "@loopback/core": "^6.0.2", "@loopback/openapi-v3": "^10.0.2", - "@loopback/repository": "^7.0.2", + "@loopback/repository": "^7.0.4", "@loopback/rest": "^14.0.2", "@loopback/rest-explorer": "^7.0.2", "@loopback/service-proxy": "^7.0.2", @@ -84,6 +84,7 @@ "@types/jsonwebtoken": "^9.0.5", "dotenv": "^16.0.3", "dotenv-extended": "^2.9.0", + "loopback4-billing": "file:loopback4-billing-0.0.1.tgz", "loopback-connector-postgresql": "^7.1.1", "loopback4-authentication": "^12.0.2", "loopback4-authorization": "^7.0.2", diff --git a/services/subscription-service/sourceloop-ctrl-plane-subscription-service-0.2.1.tgz b/services/subscription-service/sourceloop-ctrl-plane-subscription-service-0.2.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..85cab8072197283f079850fcab7111aad6353cd3 GIT binary patch literal 72945 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 E0Po6R@c;k- literal 0 HcmV?d00001 diff --git a/services/subscription-service/src/component.ts b/services/subscription-service/src/component.ts index 9455848..4aee3f0 100644 --- a/services/subscription-service/src/component.ts +++ b/services/subscription-service/src/component.ts @@ -2,68 +2,83 @@ // // This software is released under the MIT License. // https://opensource.org/licenses/MIT + +import {inject, Binding} from '@loopback/context'; import { - Binding, Component, - ControllerClass, CoreBindings, - inject, ProviderMap, ServiceOrProviderClass, + ControllerClass, } from '@loopback/core'; -import {Class, Model, Repository} from '@loopback/repository'; +import {Class, Repository, Model} from '@loopback/repository'; import {RestApplication} from '@loopback/rest'; import { - BearerVerifierBindings, - BearerVerifierComponent, - BearerVerifierConfig, - BearerVerifierType, CoreComponent, SECURITY_SCHEME_SPEC, ServiceSequence, + BearerVerifierBindings, + BearerVerifierType, + BearerVerifierConfig, + BearerVerifierComponent, } from '@sourceloop/core'; +import { + FeatureToggleBindings, + FeatureToggleServiceComponent, +} from '@sourceloop/feature-toggle-service'; +import {BillingComponent} from 'loopback4-billing'; import {AuthenticationComponent} from 'loopback4-authentication'; import { AuthorizationBindings, AuthorizationComponent, } from 'loopback4-authorization'; -import {SubscriptionServiceBindings} from './keys'; -import {ISubscriptionServiceConfig} from './types'; -import { - BillingCycleRepository, - CurrencyRepository, - PlanRepository, - PlanSizesRepository, - ResourceRepository, - ServiceRepository, - SubscriptionRepository, -} from './repositories'; import { BillinCycleController, - CurrencyController, HomePageController, PingController, + CurrencyController, PlanController, - PlanFeaturesController, - PlanSizesController, - PlanSubscriptionController, ResourceController, ServiceController, SubscriptionController, + PlanSubscriptionController, + PlanSizesController, + PlanFeaturesController, } from './controllers'; +import { + SubscriptionServiceBindings, + SYSTEM_USER, + WEBHOOK_VERIFIER, +} from './keys'; import { BillingCycle, Currency, Plan, - PlanSizes, Resource, + BillingCustomer, + Invoice, Service, Subscription, + PlanSizes, } from './models'; import { - FeatureToggleBindings, - FeatureToggleServiceComponent, -} from '@sourceloop/feature-toggle-service'; + BillingCycleRepository, + CurrencyRepository, + PlanRepository, + ResourceRepository, + ServiceRepository, + SubscriptionRepository, + PlanSizesRepository, + BillingCustomerRepository, + InvoiceRepository, +} from './repositories'; +import {ISubscriptionServiceConfig} from './types'; +import {WebhookVerifierProvider} from './interceptors/webhook-verifier.interceptor'; +import {SystemUserProvider} from './providers'; +import {BillingCustomerController} from './controllers/billing-customer.controller'; +import {BillingInvoiceController} from './controllers/billing-invoice.controller'; +import {BillingPaymentSourceController} from './controllers/billing-payment-source.controller'; +import {WebhookController} from './controllers/webhook.controller'; export class SubscriptionServiceComponent implements Component { constructor( @@ -82,6 +97,7 @@ export class SubscriptionServiceComponent implements Component { .bind(FeatureToggleBindings.Config) .to({bindControllers: true, useCustomSequence: true}); this.application.component(FeatureToggleServiceComponent); + this.application.component(BillingComponent); this.application.api({ openapi: '3.0.0', @@ -109,6 +125,8 @@ export class SubscriptionServiceComponent implements Component { ServiceRepository, SubscriptionRepository, PlanSizesRepository, + BillingCustomerRepository, + InvoiceRepository, ]; this.models = [ @@ -116,10 +134,17 @@ export class SubscriptionServiceComponent implements Component { Currency, Plan, Resource, + BillingCustomer, + Invoice, Service, Subscription, PlanSizes, ]; + this.bindings = [ + Binding.bind(WEBHOOK_VERIFIER).toProvider(WebhookVerifierProvider), + + Binding.bind(SYSTEM_USER).toProvider(SystemUserProvider), + ]; this.controllers = [ BillinCycleController, @@ -133,6 +158,10 @@ export class SubscriptionServiceComponent implements Component { PlanSubscriptionController, PlanSizesController, PlanFeaturesController, + BillingCustomerController, + BillingInvoiceController, + BillingPaymentSourceController, + WebhookController, ]; } diff --git a/services/subscription-service/src/controllers/billing-customer.controller.ts b/services/subscription-service/src/controllers/billing-customer.controller.ts new file mode 100644 index 0000000..df3ea0e --- /dev/null +++ b/services/subscription-service/src/controllers/billing-customer.controller.ts @@ -0,0 +1,211 @@ +import {BillingComponentBindings, IService} from 'loopback4-billing'; +import {inject} from '@loopback/core'; +import {Filter, repository} from '@loopback/repository'; +import { + del, + get, + getModelSchemaRef, + param, + patch, + post, + requestBody, +} from '@loopback/rest'; +import {OPERATION_SECURITY_SPEC, STATUS_CODE} from '@sourceloop/core'; +import {authenticate, STRATEGY} from 'loopback4-authentication'; +import {authorize} from 'loopback4-authorization'; +import {AddressDto} from '../models'; +import {BillingCustomer} from '../models/billing-customer.model'; +import {CustomerDto} from '../models/dto/customer-dto.model'; +import {PermissionKey} from '../permissions'; +import {InvoiceRepository} from '../repositories'; +import {BillingCustomerRepository} from '../repositories/billing-customer.repository'; + +const basePath = '/billing-customer'; +export class BillingCustomerController { + constructor( + @repository(BillingCustomerRepository) + public billingCustomerRepository: BillingCustomerRepository, + @repository(InvoiceRepository) + public invoiceRepository: InvoiceRepository, + @inject(BillingComponentBindings.BillingProvider) + private readonly billingProvider: IService, + ) {} + + @authorize({ + permissions: [PermissionKey.CreateBillingCustomer], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @post(basePath, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'BillingCustomer model instance', + content: { + 'application/json': { + schema: getModelSchemaRef(CustomerDto, { + title: 'NewBillingCustomer', + }), + }, + }, + }, + }, + }) + async create( + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(CustomerDto, { + title: 'NewCustomer', + exclude: ['id'], + }), + }, + }, + }) + customerDto: Omit, + @param.header.string('tenantId') tenantId: string, + ): Promise { + const customer = await this.billingProvider.createCustomer(customerDto); + await this.billingCustomerRepository.create( + new BillingCustomer({ + tenantId, + customerId: customer.id, + }), + ); + return new CustomerDto({ + id: customer.id, + firstName: customer.firstName, + lastName: customer.lastName, + email: customer.email, + company: customer.company, + phone: customer.phone, + billingAddress: new AddressDto({ + firstName: customer.billingAddress?.firstName, + lastName: customer.billingAddress?.lastName, + email: customer.billingAddress?.email, + company: customer.billingAddress?.company, + phone: customer.billingAddress?.phone, + city: customer.billingAddress?.city, + state: customer.billingAddress?.state, + zip: customer.billingAddress?.zip, + country: customer.billingAddress?.country, + }), + }); + } + + @authorize({ + permissions: [PermissionKey.GetBillingCustomer], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @get(basePath, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'BillingCustomer model ', + content: {'application/json': {schema: getModelSchemaRef(CustomerDto)}}, + }, + }, + }) + async getCustomer( + @param.filter(BillingCustomer) filter?: Filter, + ): Promise<{customerDetails: CustomerDto; info: BillingCustomer}> { + const customers = await this.billingCustomerRepository.find(filter); + if (customers.length === 0) { + throw new Error('Customer is not present'); + } + + const customer = await this.billingProvider.getCustomers( + customers[0].customerId, + ); + return { + customerDetails: new CustomerDto({ + firstName: customer.firstName, + lastName: customer.lastName, + email: customer.email, + company: customer.company, + phone: customer.phone, + billingAddress: new AddressDto({ + firstName: customer.billingAddress?.firstName, + lastName: customer.billingAddress?.lastName, + email: customer.billingAddress?.email, + company: customer.billingAddress?.company, + phone: customer.billingAddress?.phone, + city: customer.billingAddress?.city, + state: customer.billingAddress?.state, + zip: customer.billingAddress?.zip, + country: customer.billingAddress?.country, + }), + }), + info: customers[0], + }; + } + + @authorize({ + permissions: [PermissionKey.UpdateBillingCustomer], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @patch(`${basePath}/{tenantId}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'BillingCustomer PATCH success', + }, + }, + }) + async updateById( + @param.path.string('tenantId') tenantId: string, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(CustomerDto, {partial: true}), + }, + }, + }) + customerDto: Partial, + ): Promise { + const customers = await this.billingCustomerRepository.find({ + where: {tenantId: tenantId}, + }); + + if (customers.length === 0) { + throw new Error(`Customer with tenantId ${tenantId} is not present`); + } + await this.billingProvider.updateCustomerById( + customers[0].customerId, + customerDto, + ); + } + + @authorize({ + permissions: [PermissionKey.DeleteBillingCustomer], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @del(`${basePath}/{tenantId}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'BillingCustomer DELETE success', + }, + }, + }) + async deleteById( + @param.path.string('tenantId') tenantId: string, + ): Promise { + const customer = await this.billingCustomerRepository.find({ + where: {tenantId: tenantId}, + }); + if (customer.length === 0) { + throw new Error(' Customer with tenantId is not present'); + } + await this.billingProvider.deleteCustomer(customer[0].customerId); + await this.invoiceRepository.deleteAll({billingCustomerId: customer[0].id}); + await this.billingCustomerRepository.deleteById(customer[0].id); + } +} diff --git a/services/subscription-service/src/controllers/billing-invoice.controller.ts b/services/subscription-service/src/controllers/billing-invoice.controller.ts new file mode 100644 index 0000000..83b4f61 --- /dev/null +++ b/services/subscription-service/src/controllers/billing-invoice.controller.ts @@ -0,0 +1,227 @@ +import {BillingComponentBindings, IService} from 'loopback4-billing'; +import {inject} from '@loopback/core'; +import {repository} from '@loopback/repository'; +import { + del, + get, + getModelSchemaRef, + param, + patch, + post, + requestBody, +} from '@loopback/rest'; +import {OPERATION_SECURITY_SPEC, STATUS_CODE} from '@sourceloop/core'; +import {authenticate, STRATEGY} from 'loopback4-authentication'; +import {authorize} from 'loopback4-authorization'; +import {AddressDto, ChargeDto} from '../models'; +import {InvoiceDto} from '../models/dto/invoice-dto.model'; +import {TransactionDto} from '../models/dto/transaction-dto.model'; +import {PermissionKey} from '../permissions'; +import {InvoiceRepository} from '../repositories'; +import {BillingCustomerRepository} from '../repositories/billing-customer.repository'; + +const basePath = '/billing-invoice'; +export class BillingInvoiceController { + constructor( + @repository(BillingCustomerRepository) + public billingCustomerRepository: BillingCustomerRepository, + @repository(InvoiceRepository) + public invoiceRepository: InvoiceRepository, + @inject(BillingComponentBindings.BillingProvider) + private readonly billingProvider: IService, + ) {} + + @authorize({ + permissions: [PermissionKey.CreateBillingInvoice], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @post(basePath, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'invoice model instance', + content: {'application/json': {schema: getModelSchemaRef(InvoiceDto)}}, + }, + }, + }) + async create( + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(InvoiceDto, { + title: 'newInvoice', + exclude: ['id', 'status'], + }), + }, + }, + }) + invoiceDto: Omit, + ): Promise { + const customer = await this.billingCustomerRepository.find({ + where: {customerId: invoiceDto.customerId}, + }); + + if (customer.length === 0) { + throw new Error(' Customer with tenantId is not present'); + } + const invoice = await this.billingProvider.createInvoice(invoiceDto); + const charges = invoice.charges?.map( + charge => + new ChargeDto({amount: charge.amount, description: charge.description}), + ); + + const invoiceInfo = await this.invoiceRepository.create({ + invoiceId: invoice.id, + invoiceStatus: invoice.status, + billingCustomerId: customer[0].id, + }); + return new InvoiceDto({ + id: invoiceInfo.id, // passed the id of invoice info created in our db, to setup relation between subscription and invoice + customerId: invoice.customerId, + charges: charges, + status: invoice.status, + shippingAddress: new AddressDto({ + firstName: invoice.shippingAddress?.firstName ?? '', + lastName: invoice.shippingAddress?.lastName ?? '', + email: invoice.shippingAddress?.email ?? '', + company: invoice.shippingAddress?.company, + phone: invoice.shippingAddress?.phone, + city: invoice.shippingAddress?.city ?? '', + state: invoice.shippingAddress?.state ?? '', + zip: invoice.shippingAddress?.zip ?? '', + country: invoice.shippingAddress?.country ?? '', + }), + options: invoice.options, + }); + } + + @authorize({ + permissions: [PermissionKey.GetBillingInvoice], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @get(`${basePath}/{invoiceId}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'get invoice', + content: {'application/json': {schema: getModelSchemaRef(InvoiceDto)}}, + }, + }, + }) + async getInvoice( + @param.path.string('invoiceId') invoiceId: string, + ): Promise { + const invoice = await this.billingProvider.retrieveInvoice(invoiceId); + const charges = invoice.charges?.map( + charge => + new ChargeDto({amount: charge.amount, description: charge.description}), + ); + return new InvoiceDto({ + customerId: invoice.customerId, + charges: charges, + status: invoice.status, + shippingAddress: new AddressDto({ + firstName: invoice.shippingAddress?.firstName ?? '', + lastName: invoice.shippingAddress?.lastName ?? '', + email: invoice.shippingAddress?.email ?? '', + company: invoice.shippingAddress?.company, + phone: invoice.shippingAddress?.phone, + city: invoice.shippingAddress?.city ?? '', + state: invoice.shippingAddress?.state ?? '', + zip: invoice.shippingAddress?.zip ?? '', + country: invoice.shippingAddress?.country ?? '', + }), + options: invoice.options, + }); + } + + @authorize({ + permissions: [PermissionKey.UpdateBillingInvoice], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @patch(`${basePath}/{invoiceId}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'Billing Invoice PATCH success', + }, + }, + }) + async updateById( + @param.path.string('invoiceId') invoiceId: string, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(InvoiceDto, {partial: true}), + }, + }, + }) + invoiceDto: Partial, + ): Promise { + await this.billingProvider.updateInvoice(invoiceId, invoiceDto); + } + + @authorize({ + permissions: [PermissionKey.CreateBillingInvoice], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @post(`${basePath}/{invoiceId}/payments`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'invoice model instance', + }, + }, + }) + async applyPaymentForInvoice( + @param.path.string('invoiceId') invoiceId: string, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(TransactionDto, {partial: true}), + }, + }, + }) + transactionDto: TransactionDto, + ): Promise { + const invoiceInfo = await this.invoiceRepository.findById(invoiceId); + await this.billingProvider.applyPaymentSourceForInvoice( + invoiceInfo.invoiceId, + transactionDto, + ); + } + + @authorize({ + permissions: [PermissionKey.DeleteBillingPaymentSource], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @del(`${basePath}/{invoiceId}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'Billing Invoice DELETE success', + }, + }, + }) + async deleteById( + @param.path.string('invoiceId') invoiceId: string, + ): Promise { + const invoice = await this.invoiceRepository.find({ + where: {invoiceId: invoiceId}, + }); + if (invoice.length === 0) + throw new Error(' invoice with invoiceId is not present'); + await this.billingProvider.deleteInvoice(invoiceId); + await this.invoiceRepository.deleteById(invoice[0].id); + } +} diff --git a/services/subscription-service/src/controllers/billing-payment-source.controller.ts b/services/subscription-service/src/controllers/billing-payment-source.controller.ts new file mode 100644 index 0000000..28fc6c3 --- /dev/null +++ b/services/subscription-service/src/controllers/billing-payment-source.controller.ts @@ -0,0 +1,138 @@ +import {BillingComponentBindings, IService} from 'loopback4-billing'; +import {inject} from '@loopback/core'; +import {repository} from '@loopback/repository'; +import { + del, + get, + getModelSchemaRef, + param, + post, + requestBody, +} from '@loopback/rest'; +import {OPERATION_SECURITY_SPEC, STATUS_CODE} from '@sourceloop/core'; +import {authenticate, STRATEGY} from 'loopback4-authentication'; +import {authorize} from 'loopback4-authorization'; +import {PaymentSourceDto} from '../models/dto/payment-dto.model'; +import {PermissionKey} from '../permissions'; +import {InvoiceRepository} from '../repositories'; +import {BillingCustomerRepository} from '../repositories/billing-customer.repository'; + +const basePath = '/billing-payment-source'; +export class BillingPaymentSourceController { + constructor( + @repository(BillingCustomerRepository) + public billingCustomerRepository: BillingCustomerRepository, + @repository(InvoiceRepository) + public invoiceRepository: InvoiceRepository, + @inject(BillingComponentBindings.BillingProvider) + private readonly billingProvider: IService, + ) {} + + @authorize({ + permissions: [PermissionKey.CreateBillingPaymentSource], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @post(basePath, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'Payment model instance', + content: { + 'application/json': {schema: getModelSchemaRef(PaymentSourceDto)}, + }, + }, + }, + }) + async create( + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(PaymentSourceDto, { + title: 'NewPaymentSource', + exclude: ['id'], + }), + }, + }, + }) + paymentSourceDto: PaymentSourceDto, + ): Promise { + const customer = await this.billingCustomerRepository.find({ + where: {customerId: paymentSourceDto.customerId}, + }); + + if (customer.length === 0) { + throw new Error(' Customer with tenantId is not present'); + } + const paymentSource = + await this.billingProvider.createPaymentSource(paymentSourceDto); + await this.billingCustomerRepository.updateById(customer[0].id, { + paymentSourceId: paymentSource.id, + }); + return new PaymentSourceDto({ + id: paymentSource.id, + customerId: paymentSource.customerId, + card: paymentSource.card, + }); + } + + @authorize({ + permissions: [PermissionKey.GetBillingPaymentSource], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @get(basePath, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'get payment source', + content: { + 'application/json': {schema: getModelSchemaRef(PaymentSourceDto)}, + }, + }, + }, + }) + async getPaymentSource( + @param.path.string('paymentSourceId') paymentSourceId: string, + ): Promise { + const paymentSource = + await this.billingProvider.retrievePaymentSource(paymentSourceId); + return new PaymentSourceDto({ + id: paymentSource.id, + customerId: paymentSource.customerId, + card: paymentSource.card, + }); + } + + @authorize({ + permissions: [PermissionKey.DeleteBillingPaymentSource], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @del(`${basePath}/{paymentSourceId}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'Billing Payment Source DELETE success', + }, + }, + }) + async deleteById( + @param.path.string('paymentSourceId') paymentSourceId: string, + ): Promise { + const customer = await this.billingCustomerRepository.find({ + where: {paymentSourceId: paymentSourceId}, + }); + + if (customer.length === 0) { + throw new Error(' Customer with tenantId is not present'); + } + await this.billingProvider.deletePaymentSource(paymentSourceId); + await this.billingCustomerRepository.updateById(customer[0].id, { + paymentSourceId: undefined, + }); + } +} diff --git a/services/subscription-service/src/controllers/index.ts b/services/subscription-service/src/controllers/index.ts index 7910a50..4ebd610 100644 --- a/services/subscription-service/src/controllers/index.ts +++ b/services/subscription-service/src/controllers/index.ts @@ -9,3 +9,4 @@ export * from './currency.controller'; export * from './plan-subscription.controller'; export * from './plan-sizes.controller'; export * from './plan-features.controller'; +export * from './subscription-invoice.controller'; diff --git a/services/subscription-service/src/controllers/subscription-invoice.controller.ts b/services/subscription-service/src/controllers/subscription-invoice.controller.ts new file mode 100644 index 0000000..96b541e --- /dev/null +++ b/services/subscription-service/src/controllers/subscription-invoice.controller.ts @@ -0,0 +1,29 @@ +import {repository} from '@loopback/repository'; +import {param, get, getModelSchemaRef} from '@loopback/rest'; +import {Subscription, Invoice} from '../models'; +import {SubscriptionRepository} from '../repositories'; + +export class SubscriptionInvoiceController { + constructor( + @repository(SubscriptionRepository) + public subscriptionRepository: SubscriptionRepository, + ) {} + + @get('/subscriptions/{id}/invoice', { + responses: { + '200': { + description: 'Invoice belonging to Subscription', + content: { + 'application/json': { + schema: getModelSchemaRef(Invoice), + }, + }, + }, + }, + }) + async getInvoice( + @param.path.string('id') id: typeof Subscription.prototype.id, + ): Promise { + return this.subscriptionRepository.invoice(id); + } +} diff --git a/services/subscription-service/src/controllers/webhook.controller.ts b/services/subscription-service/src/controllers/webhook.controller.ts new file mode 100644 index 0000000..aa97d67 --- /dev/null +++ b/services/subscription-service/src/controllers/webhook.controller.ts @@ -0,0 +1,36 @@ +import {intercept} from '@loopback/core'; +import {repository} from '@loopback/repository'; +import {post, requestBody} from '@loopback/rest'; +import {authorize} from 'loopback4-authorization'; +import {WEBHOOK_VERIFIER} from '../keys'; +import {InvoiceRepository} from '../repositories'; +import {BillingCustomerRepository} from '../repositories/billing-customer.repository'; +import {IContent, IPayload} from '../types'; + +export class WebhookController { + constructor( + @repository(BillingCustomerRepository) + public billingCustomerRepository: BillingCustomerRepository, + @repository(InvoiceRepository) + public invoiceRepository: InvoiceRepository, + ) {} + + @authorize({ + permissions: ['*'], + }) + @intercept(WEBHOOK_VERIFIER) + @post('/webhooks/billing-payment') + async handleWebhook(@requestBody() payload: IPayload): Promise { + const content = payload.content; + await this.handlePayment(content); + } + + private async handlePayment(content: IContent): Promise { + const invoice = await this.invoiceRepository.find({ + where: {invoiceId: content.invoice.id}, + }); + await this.invoiceRepository.updateById(invoice[0].id, { + invoiceStatus: content.invoice.status, + }); + } +} diff --git a/services/subscription-service/src/interceptors/index.ts b/services/subscription-service/src/interceptors/index.ts new file mode 100644 index 0000000..dc511f6 --- /dev/null +++ b/services/subscription-service/src/interceptors/index.ts @@ -0,0 +1 @@ +export * from './webhook-verifier.interceptor'; diff --git a/services/subscription-service/src/interceptors/webhook-verifier.interceptor.ts b/services/subscription-service/src/interceptors/webhook-verifier.interceptor.ts new file mode 100644 index 0000000..26a204e --- /dev/null +++ b/services/subscription-service/src/interceptors/webhook-verifier.interceptor.ts @@ -0,0 +1,51 @@ +import { + Interceptor, + InvocationContext, + Provider, + Setter, + ValueOrPromise, + inject, +} from '@loopback/core'; +import {HttpErrors, RequestContext} from '@loopback/rest'; +import {ILogger, LOGGER} from '@sourceloop/core'; +import {AuthenticationBindings, IAuthUser} from 'loopback4-authentication'; +import {SYSTEM_USER} from '../keys'; + +export class WebhookVerifierProvider implements Provider { + constructor( + @inject(LOGGER.LOGGER_INJECT) + private readonly logger: ILogger, + @inject.setter(AuthenticationBindings.CURRENT_USER) + private readonly setCurrentUser: Setter, + @inject(SYSTEM_USER) + private readonly systemUser: IAuthUser, + ) {} + + value() { + return this.intercept.bind(this); + } + + async intercept( + invocationCtx: InvocationContext, + next: () => ValueOrPromise, + ) { + const {request} = invocationCtx.parent as RequestContext; + const authHeader = request.headers['authorization']; + const username = process.env.WEBHOOK_USERNAME; + const password = process.env.WEBHOOK_PASSWORD; + const expectedAuthHeader = + 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'); + + try { + if (!authHeader || authHeader !== expectedAuthHeader) { + throw new HttpErrors.Unauthorized('Invalid authorization.'); + } + } catch (e) { + this.logger.error(e); + throw new HttpErrors.Unauthorized(); + } + + this.setCurrentUser(this.systemUser); + return next(); + } +} diff --git a/services/subscription-service/src/keys.ts b/services/subscription-service/src/keys.ts index 3801d39..6c541cf 100644 --- a/services/subscription-service/src/keys.ts +++ b/services/subscription-service/src/keys.ts @@ -1,8 +1,9 @@ -import {BindingKey} from '@loopback/core'; +import {BindingKey, Interceptor} from '@loopback/core'; import {ISubscriptionServiceConfig} from './types'; import {BINDING_PREFIX} from '@sourceloop/core'; import {VerifyFunction} from 'loopback4-authentication'; import {AnyObject} from '@loopback/repository'; +import {IAuthUser} from 'loopback4-authorization'; export namespace SubscriptionServiceBindings { export const Config = BindingKey.create( @@ -10,9 +11,20 @@ export namespace SubscriptionServiceBindings { ); } +export const WEBHOOK_VERIFIER = BindingKey.create( + 'sf.webhook.verifier', +); + +/** + * Binding key for the lead token verifier. + */ export const LEAD_TOKEN_VERIFIER = BindingKey.create< VerifyFunction.BearerFn >('sf.user.lead.verifier'); + /** - * Binding key for the lead token verifier. + * Binding key for the system user. */ +export const SYSTEM_USER = BindingKey.create( + 'sf.user.system', +); diff --git a/services/subscription-service/src/models/billing-customer.model.ts b/services/subscription-service/src/models/billing-customer.model.ts new file mode 100644 index 0000000..47bc964 --- /dev/null +++ b/services/subscription-service/src/models/billing-customer.model.ts @@ -0,0 +1,44 @@ +import {model, property, hasMany} from '@loopback/repository'; +import {UserModifiableEntity} from '@sourceloop/core'; +import {Invoice} from './invoice.model'; + +@model({ + name: 'billing_customer', + description: 'contacts belonging to a tenant', +}) +export class BillingCustomer extends UserModifiableEntity { + @property({ + type: 'string', + id: true, + generated: true, + }) + id?: string; + + @property({ + type: 'string', + name: 'tenant_id', + required: true, + }) + tenantId: string; // tenantId of customer + + @property({ + type: 'string', + name: 'customer_id', + required: true, + }) + customerId: string; // id of customer generated on third party billing module + + @property({ + type: 'string', + name: 'payment_source_id', + }) + paymentSourceId?: string; + + // Define the hasMany relation + @hasMany(() => Invoice, {keyTo: 'billingCustomerId'}) + invoices: Invoice[]; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/address-dto.model.ts b/services/subscription-service/src/models/dto/address-dto.model.ts new file mode 100644 index 0000000..248ea5c --- /dev/null +++ b/services/subscription-service/src/models/dto/address-dto.model.ts @@ -0,0 +1,66 @@ +import {Entity, model, property} from '@loopback/repository'; +import {Options} from 'loopback4-billing'; + +@model() +export class AddressDto extends Entity { + @property({ + type: 'string', + name: 'first_name', + }) + firstName: string; + + @property({ + type: 'string', + name: 'last_name', + }) + lastName: string; + + @property({ + type: 'string', + required: true, + }) + email: string; + + @property({ + type: 'string', + }) + company?: string; + + @property({ + type: 'string', + }) + phone?: string; + + @property({ + type: 'string', + required: true, + }) + city: string; + + @property({ + type: 'string', + required: true, + }) + state: string; + + @property({ + type: 'string', + required: true, + }) + zip: string; + + @property({ + type: 'string', + required: true, + }) + country: string; + + @property({ + type: 'object', + }) + options?: Options; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/charge-dto.model.ts b/services/subscription-service/src/models/dto/charge-dto.model.ts new file mode 100644 index 0000000..d17f0c2 --- /dev/null +++ b/services/subscription-service/src/models/dto/charge-dto.model.ts @@ -0,0 +1,20 @@ +import {Entity, model, property} from '@loopback/repository'; + +@model() +export class ChargeDto extends Entity { + @property({ + type: 'number', + required: true, + }) + amount: number; + + @property({ + type: 'string', + required: true, + }) + description: string; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/customer-dto.model.ts b/services/subscription-service/src/models/dto/customer-dto.model.ts new file mode 100644 index 0000000..d00de82 --- /dev/null +++ b/services/subscription-service/src/models/dto/customer-dto.model.ts @@ -0,0 +1,75 @@ +import {model, Model, property} from '@loopback/repository'; +import {AddressDto} from './address-dto.model'; +import {Options} from 'loopback4-billing'; + +@model({ + name: 'customer_dto', +}) +export class CustomerDto extends Model { + @property({ + type: 'string', + name: 'id', + }) + id?: string; + + @property({ + type: 'string', + name: 'first_name', + }) + firstName: string; + + @property({ + type: 'string', + name: 'last_name', + }) + lastName: string; + + @property({ + type: 'string', + name: 'email', + }) + email: string; + + @property({ + type: 'string', + name: 'company', + }) + company: string; + + @property({ + type: 'string', + name: 'phone', + }) + phone: string; + + @property({ + type: AddressDto, + name: 'billing_address', + }) + billingAddress: AddressDto; + + @property({ + type: 'object', + }) + options?: Options; + + constructor(data?: Partial) { + super(data); + } +} + +// this refers to the billing address and shipping address. +export interface IAddress { + firstName: string; + lastName: string; + email: string; + company?: string; + phone?: string; + line1?: string; + line2?: string; + line3?: string; + city: string; + state: string; + zip: string; + coutnry: string; +} diff --git a/services/subscription-service/src/models/dto/index.ts b/services/subscription-service/src/models/dto/index.ts new file mode 100644 index 0000000..3037b0e --- /dev/null +++ b/services/subscription-service/src/models/dto/index.ts @@ -0,0 +1,5 @@ +export * from './address-dto.model'; +export * from './charge-dto.model'; +export * from './customer-dto.model'; +export * from './invoice-dto.model'; +export * from './payment-dto.model'; diff --git a/services/subscription-service/src/models/dto/invoice-dto.model.ts b/services/subscription-service/src/models/dto/invoice-dto.model.ts new file mode 100644 index 0000000..a1803f0 --- /dev/null +++ b/services/subscription-service/src/models/dto/invoice-dto.model.ts @@ -0,0 +1,50 @@ +import {model, Model, property} from '@loopback/repository'; +import {Options} from 'loopback4-billing'; +import {AddressDto} from './address-dto.model'; +import {ChargeDto} from './charge-dto.model'; +import {InvoiceStatus} from '../../types'; + +@model({ + name: 'invoice_dto', +}) +export class InvoiceDto extends Model { + @property({ + type: 'string', + name: 'id', + }) + id?: string; + + @property({ + type: 'string', + name: 'customer_id', + }) + customerId: string; + + @property({ + type: 'object', + }) + options?: Options; + + @property({ + type: AddressDto, + name: 'shipping_address', + }) + shippingAddress: AddressDto; + + @property({ + type: 'array', + itemType: ChargeDto, + name: 'charges', + }) + charges: ChargeDto[]; + + @property({ + type: 'string', + name: 'status', + }) + status?: InvoiceStatus; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/dto/payment-dto.model.ts b/services/subscription-service/src/models/dto/payment-dto.model.ts new file mode 100644 index 0000000..af719db --- /dev/null +++ b/services/subscription-service/src/models/dto/payment-dto.model.ts @@ -0,0 +1,55 @@ +import {model, Model, property} from '@loopback/repository'; + +@model({ + name: 'payment_source_dto', +}) +export class PaymentSourceDto extends Model { + @property({ + type: 'string', + name: 'id', + }) + id?: string; + + @property({ + type: 'string', + name: 'customer_id', + }) + customerId: string; + + @property({ + type: 'object', + name: 'card', + required: true, + jsonSchema: { + type: 'object', + properties: { + gatewayAccountId: {type: 'string'}, + number: {type: 'string'}, + expiryMonth: {type: 'number'}, + expiryear: {type: 'number'}, + cvv: {type: 'string'}, + }, + required: [ + 'gatewayAccountId', + 'number', + 'expiryMonth', + 'expiryYear', + 'cvv', + ], + }, + }) + card: ICard; + + constructor(data?: Partial) { + super(data); + } +} + +// this refers to the card +export interface ICard { + gatewayAccountId: string; + number: string; + expiryMonth: number; + expiryYear: number; + cvv: string; +} diff --git a/services/subscription-service/src/models/dto/transaction-dto.model.ts b/services/subscription-service/src/models/dto/transaction-dto.model.ts new file mode 100644 index 0000000..eb5cef8 --- /dev/null +++ b/services/subscription-service/src/models/dto/transaction-dto.model.ts @@ -0,0 +1,110 @@ +import {Entity, model, property} from '@loopback/repository'; +export enum PaymentMethodEnum { + Cash = 'cash', + Check = 'check', + BankTranser = 'bank_transfer', + Other = 'other', + Custom = 'custom', + PaymentSource = 'payment_source', +} + +@model() +export class TransactionDto extends Entity { + @property({ + type: 'number', + required: false, + jsonSchema: { + minimum: 0, + }, + }) + amount?: number; + + @property({ + type: 'string', + description: 'payment method', + required: true, + jsonSchema: { + enum: Object.values(PaymentMethodEnum), + }, + }) + paymentMethod: PaymentMethodEnum; + + @property({ + type: 'string', + required: false, + }) + paymentSourceId?: string; // Optional + + @property({ + type: 'string', + required: false, + jsonSchema: { + maxLength: 100, + }, + }) + referenceNumber?: string; // Optional, max 100 chars + + @property({ + type: 'string', + required: false, + jsonSchema: { + maxLength: 50, + }, + }) + customPaymentMethodId?: string; // Optional, max 50 chars + + @property({ + type: 'string', + required: false, + jsonSchema: { + maxLength: 100, + }, + }) + idAtGateway?: string; // Optional, max 100 chars + + @property({ + type: 'string', + required: false, + jsonSchema: { + enum: ['success', 'failure'], + }, + }) + status?: 'success' | 'failure'; // Optional + + @property({ + type: 'number', + required: false, + }) + date?: number; // Optional, timestamp in seconds (UTC) + + @property({ + type: 'string', + required: false, + jsonSchema: { + maxLength: 100, + }, + }) + errorCode?: string; // Optional, max 100 chars + + @property({ + type: 'string', + required: false, + jsonSchema: { + maxLength: 65000, + }, + }) + errorText?: string; // Optional, max 65k chars + + @property({ + type: 'string', + required: false, + jsonSchema: { + maxLength: 300, + }, + }) + comment?: string; // Optional, max 300 chars + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/index.ts b/services/subscription-service/src/models/index.ts index 0dc32c6..8753b60 100644 --- a/services/subscription-service/src/models/index.ts +++ b/services/subscription-service/src/models/index.ts @@ -5,3 +5,6 @@ export * from './resource.model'; export * from './billing-cycle.model'; export * from './currency.model'; export * from './plan-sizes.model'; +export * from './dto'; +export * from './billing-customer.model'; +export * from './invoice.model'; diff --git a/services/subscription-service/src/models/invoice.model.ts b/services/subscription-service/src/models/invoice.model.ts new file mode 100644 index 0000000..e481409 --- /dev/null +++ b/services/subscription-service/src/models/invoice.model.ts @@ -0,0 +1,40 @@ +import {model, property} from '@loopback/repository'; +import {UserModifiableEntity} from '@sourceloop/core'; +import {InvoiceStatus} from '../types'; +@model({ + name: 'invoice', + description: 'invoice for a customer', +}) +export class Invoice extends UserModifiableEntity { + @property({ + type: 'string', + id: true, + generated: true, + }) + id?: string; + + @property({ + type: 'string', + name: 'invoice_id', + required: true, + }) + invoiceId: string; + + @property({ + type: 'string', + name: 'invoice_status', + description: 'payment or invoice status', + }) + invoiceStatus?: InvoiceStatus; + + @property({ + type: 'string', + name: 'billing_customer_id', + required: true, + }) + billingCustomerId: string; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/subscription-service/src/models/subscription.model.ts b/services/subscription-service/src/models/subscription.model.ts index 3b2f834..71c8381 100644 --- a/services/subscription-service/src/models/subscription.model.ts +++ b/services/subscription-service/src/models/subscription.model.ts @@ -3,6 +3,7 @@ import {SubscriptionStatus} from '../enums/subscription-status.enum'; import {numericEnumValues} from '../utils'; import {UserModifiableEntity} from '@sourceloop/core'; import {Plan} from './plan.model'; +import {Invoice} from './invoice.model'; @model({ name: 'subscriptions', @@ -53,6 +54,12 @@ export class Subscription extends UserModifiableEntity { }) planId: string; + @belongsTo(() => Invoice, undefined, { + description: 'invoice id of the subscription', + name: 'invoice_id', + }) + invoiceId: string; + constructor(data?: Partial) { super(data); } diff --git a/services/subscription-service/src/permissions.ts b/services/subscription-service/src/permissions.ts index d1b5c60..e167930 100644 --- a/services/subscription-service/src/permissions.ts +++ b/services/subscription-service/src/permissions.ts @@ -48,4 +48,16 @@ export const PermissionKey = { DeleteInvoice: '10214', ViewInvoice: '10215', CreateNotification: '2', + CreateBillingCustomer: '5321', + CreateBillingPaymentSource: '5322', + CreateBillingInvoice: '5323', + GetBillingCustomer: '5324', + GetBillingPaymentSource: '5325', + GetBillingInvoice: '5326', + UpdateBillingCustomer: '5327', + UpdateBillingPaymentSource: '5328', + UpdateBillingInvoice: '5329', + DeleteBillingCustomer: '5331', + DeleteBillingPaymentSource: '5332', + DeleteBillingInvoice: '5333', }; diff --git a/services/subscription-service/src/providers/index.ts b/services/subscription-service/src/providers/index.ts index e69de29..e1a63a7 100644 --- a/services/subscription-service/src/providers/index.ts +++ b/services/subscription-service/src/providers/index.ts @@ -0,0 +1 @@ +export * from './system-user.provider'; diff --git a/services/subscription-service/src/providers/system-user.provider.ts b/services/subscription-service/src/providers/system-user.provider.ts new file mode 100644 index 0000000..763b77c --- /dev/null +++ b/services/subscription-service/src/providers/system-user.provider.ts @@ -0,0 +1,13 @@ +import {Provider, ValueOrPromise} from '@loopback/core'; +import {IAuthUser} from 'loopback4-authorization'; +import {AnyObject} from '@loopback/repository'; +export class SystemUserProvider implements Provider { + value(): ValueOrPromise { + return { + username: 'SYSTEM', + id: process.env.SYSTEM_USER_ID!, + userTenantId: process.env.SYSTEM_USER_ID!, + permissions: [], + }; + } +} diff --git a/services/subscription-service/src/repositories/billing-customer.repository.ts b/services/subscription-service/src/repositories/billing-customer.repository.ts new file mode 100644 index 0000000..58deb2f --- /dev/null +++ b/services/subscription-service/src/repositories/billing-customer.repository.ts @@ -0,0 +1,42 @@ +import {Getter, inject} from '@loopback/core'; +import { + HasManyRepositoryFactory, + juggler, + repository, +} from '@loopback/repository'; +import { + DefaultUserModifyCrudRepository, + IAuthUserWithPermissions, +} from '@sourceloop/core'; +import {AuthenticationBindings} from 'loopback4-authentication'; +import {BillingCustomer, Invoice} from '../models'; +import {SubscriptionDbSourceName} from '../types'; +import {InvoiceRepository} from './invoice.repository'; + +export class BillingCustomerRepository extends DefaultUserModifyCrudRepository< + BillingCustomer, + typeof BillingCustomer.prototype.id, + {} +> { + public readonly invoices: HasManyRepositoryFactory< + Invoice, + typeof BillingCustomer.prototype.id + >; + + constructor( + @inject(`datasources.${SubscriptionDbSourceName}`) + dataSource: juggler.DataSource, + @repository.getter('InvoiceRepository') + protected invoiceRepositoryGetter: Getter, + + @inject.getter(AuthenticationBindings.CURRENT_USER) + public readonly getCurrentUser: Getter, + ) { + super(BillingCustomer, dataSource, getCurrentUser); + this.invoices = this.createHasManyRepositoryFactoryFor( + 'invoices', + invoiceRepositoryGetter, + ); + this.registerInclusionResolver('invoices', this.invoices.inclusionResolver); + } +} diff --git a/services/subscription-service/src/repositories/index.ts b/services/subscription-service/src/repositories/index.ts index 6a82b38..4379caa 100644 --- a/services/subscription-service/src/repositories/index.ts +++ b/services/subscription-service/src/repositories/index.ts @@ -5,3 +5,5 @@ export * from './subscription.repository'; export * from './billing-cycle.repository'; export * from './currency.repository'; export * from './plan-sizes.repository'; +export * from './billing-customer.repository'; +export * from './invoice.repository'; diff --git a/services/subscription-service/src/repositories/invoice.repository.ts b/services/subscription-service/src/repositories/invoice.repository.ts new file mode 100644 index 0000000..6cb5fbd --- /dev/null +++ b/services/subscription-service/src/repositories/invoice.repository.ts @@ -0,0 +1,24 @@ +import {Getter, inject} from '@loopback/core'; +import {juggler} from '@loopback/repository'; +import { + DefaultUserModifyCrudRepository, + IAuthUserWithPermissions, +} from '@sourceloop/core'; +import {AuthenticationBindings} from 'loopback4-authentication'; +import {Invoice} from '../models'; +import {SubscriptionDbSourceName} from '../types'; + +export class InvoiceRepository extends DefaultUserModifyCrudRepository< + Invoice, + typeof Invoice.prototype.id, + {} +> { + constructor( + @inject(`datasources.${SubscriptionDbSourceName}`) + dataSource: juggler.DataSource, + @inject.getter(AuthenticationBindings.CURRENT_USER) + public readonly getCurrentUser: Getter, + ) { + super(Invoice, dataSource, getCurrentUser); + } +} diff --git a/services/subscription-service/src/repositories/subscription.repository.ts b/services/subscription-service/src/repositories/subscription.repository.ts index 8c57007..d496b0d 100644 --- a/services/subscription-service/src/repositories/subscription.repository.ts +++ b/services/subscription-service/src/repositories/subscription.repository.ts @@ -1,6 +1,6 @@ import {inject, Getter} from '@loopback/core'; import {repository, BelongsToAccessor, juggler} from '@loopback/repository'; -import {Subscription, SubscriptionRelations, Plan} from '../models'; +import {Subscription, SubscriptionRelations, Plan, Invoice} from '../models'; import {PlanRepository} from './plan.repository'; import {AuthenticationBindings} from 'loopback4-authentication'; import { @@ -8,6 +8,7 @@ import { IAuthUserWithPermissions, } from '@sourceloop/core'; import {SubscriptionDbSourceName} from '../types'; +import {InvoiceRepository} from './invoice.repository'; export class SubscriptionRepository extends DefaultUserModifyCrudRepository< Subscription, @@ -19,6 +20,11 @@ export class SubscriptionRepository extends DefaultUserModifyCrudRepository< typeof Subscription.prototype.id >; + public readonly invoice: BelongsToAccessor< + Invoice, + typeof Subscription.prototype.id + >; + constructor( @inject(`datasources.${SubscriptionDbSourceName}`) dataSource: juggler.DataSource, @@ -26,8 +32,15 @@ export class SubscriptionRepository extends DefaultUserModifyCrudRepository< public readonly getCurrentUser: Getter, @repository.getter('PlanRepository') protected planRepositoryGetter: Getter, + @repository.getter('InvoiceRepository') + protected invoiceRepositoryGetter: Getter, ) { super(Subscription, dataSource, getCurrentUser); + this.invoice = this.createBelongsToAccessorFor( + 'invoice', + invoiceRepositoryGetter, + ); + this.registerInclusionResolver('invoice', this.invoice.inclusionResolver); this.plan = this.createBelongsToAccessorFor('plan', planRepositoryGetter); this.registerInclusionResolver('plan', this.plan.inclusionResolver); } diff --git a/services/subscription-service/src/types.ts b/services/subscription-service/src/types.ts index 7f53cd8..193cef1 100644 --- a/services/subscription-service/src/types.ts +++ b/services/subscription-service/src/types.ts @@ -2,6 +2,7 @@ // // This software is released under the MIT License. // https://opensource.org/licenses/MIT +import {IInvoice} from 'loopback4-billing'; import {IServiceConfig} from '@sourceloop/core'; // sonarignore:start @@ -24,3 +25,24 @@ export type LeadUserWithToken = { export const SubscriptionDbSourceName = 'SubscriptionDB'; // sonarignore:end + +export type InvoiceStatus = + | 'paid' + | 'posted' + | 'payment_due' + | 'not_paid' + | 'voided' + | 'pending'; + +export interface IPayload { + id: string; + // eslint-disable-next-line @typescript-eslint/naming-convention + event_type: string; + // eslint-disable-next-line @typescript-eslint/naming-convention + webhook_status: string; + content: IContent; +} + +export interface IContent { + invoice: IInvoice; +} diff --git a/services/tenant-management-service/src/models/dtos/subscription-dto.model.ts b/services/tenant-management-service/src/models/dtos/subscription-dto.model.ts index a3fbb84..f30a44b 100644 --- a/services/tenant-management-service/src/models/dtos/subscription-dto.model.ts +++ b/services/tenant-management-service/src/models/dtos/subscription-dto.model.ts @@ -42,6 +42,9 @@ export class SubscriptionDTO implements ISubscription { @property({type: 'string'}) planId: string; + @property({type: 'string'}) + invoiceId: string; + // Assuming IPlan interface is defined, you can include it here @property() plan?: IPlan; diff --git a/services/tenant-management-service/src/permissions.ts b/services/tenant-management-service/src/permissions.ts index 9da2602..4cf7dd9 100644 --- a/services/tenant-management-service/src/permissions.ts +++ b/services/tenant-management-service/src/permissions.ts @@ -28,4 +28,17 @@ export const PermissionKey = { CreateNotificationTemplate: '8001', UpdateNotificationTemplate: '8002', DeleteNotificationTemplate: '8003', + + CreateBillingCustomer: '5321', + CreateBillingPaymentSource: '5322', + CreateBillingInvoice: '5323', + GetBillingCustomer: '5324', + GetBillingPaymentSource: '5325', + GetBillingInvoice: '5326', + UpdateBillingCustomer: '5327', + UpdateBillingPaymentSource: '5328', + UpdateBillingInvoice: '5329', + DeleteBillingCustomer: '5331', + DeleteBillingPaymentSource: '5332', + DeleteBillingInvoice: '5333', }; diff --git a/services/tenant-management-service/src/services/lead-authenticator.service.ts b/services/tenant-management-service/src/services/lead-authenticator.service.ts index ee2c700..1208fbe 100644 --- a/services/tenant-management-service/src/services/lead-authenticator.service.ts +++ b/services/tenant-management-service/src/services/lead-authenticator.service.ts @@ -43,6 +43,18 @@ export class LeadAuthenticator { PermissionKey.ViewPlan, PermissionKey.ViewSubscription, PermissionKey.CreateInvoice, + PermissionKey.CreateBillingCustomer, + PermissionKey.CreateBillingPaymentSource, + PermissionKey.CreateBillingInvoice, + PermissionKey.GetBillingCustomer, + PermissionKey.GetBillingPaymentSource, + PermissionKey.GetBillingInvoice, + PermissionKey.UpdateBillingCustomer, + PermissionKey.UpdateBillingPaymentSource, + PermissionKey.UpdateBillingInvoice, + PermissionKey.DeleteBillingCustomer, + PermissionKey.DeleteBillingPaymentSource, + PermissionKey.DeleteBillingInvoice, ]); const randomKey = this.cryptoHelperService.generateRandomString( +process.env.LEAD_KEY_LENGTH!, diff --git a/services/tenant-management-service/src/services/onboarding.service.ts b/services/tenant-management-service/src/services/onboarding.service.ts index 2e11ea6..1d444c5 100644 --- a/services/tenant-management-service/src/services/onboarding.service.ts +++ b/services/tenant-management-service/src/services/onboarding.service.ts @@ -2,7 +2,9 @@ import {BindingScope, inject, injectable, service} from '@loopback/core'; import {repository} from '@loopback/repository'; import {HttpErrors} from '@loopback/rest'; import {ILogger, LOGGER} from '@sourceloop/core'; +import {TenantStatus} from '../enums'; import {Address, Contact, Lead, Tenant, TenantOnboardDTO} from '../models'; +import {CreateLeadDTO} from '../models/dtos/create-lead-dto.model'; import { AddressRepository, ContactRepository, @@ -10,10 +12,8 @@ import { TenantRepository, } from '../repositories'; import {LeadUser} from '../types'; -import {LeadAuthenticator} from './lead-authenticator.service'; -import {TenantStatus} from '../enums'; -import {CreateLeadDTO} from '../models/dtos/create-lead-dto.model'; import {hasAnyOf, weakEqual} from '../utils'; +import {LeadAuthenticator} from './lead-authenticator.service'; /** * Helper service for onboarding tenants. @@ -197,8 +197,21 @@ export class OnboardingService { }, {transaction}, ); + const res = await this.tenantRepository.findById( + tenant.id, + { + include: [ + {relation: 'contacts'}, + {relation: 'resources'}, + {relation: 'lead'}, + {relation: 'address'}, + ], + }, + {transaction}, + ); + await transaction.commit(); - return tenant; + return res; } catch (error) { await transaction.rollback(); throw error; From 144e45fb080cdf1aa3a2d9eb7ee3c70515771379 Mon Sep 17 00:00:00 2001 From: Tyagi-Sunny Date: Fri, 20 Sep 2024 13:02:57 +0530 Subject: [PATCH 2/8] feat(subscription-service): update billing package tar update billing package tar BREAKING CHANGE: yes gh-34 --- .../loopback4-billing-0.0.1.tgz | Bin 29337 -> 29504 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/services/subscription-service/loopback4-billing-0.0.1.tgz b/services/subscription-service/loopback4-billing-0.0.1.tgz index 0877da65634cda9b59fdbd8156f52c55dc9756e6..5e14d75640ecd2af1d630c09b9e14a9f2c70605c 100644 GIT binary patch delta 26347 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 delta 26199 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 GC<6eq@VmnR From 1e101254508c63bb1b70dd2c6321990d276d3512 Mon Sep 17 00:00:00 2001 From: Tyagi-Sunny Date: Fri, 20 Sep 2024 13:14:59 +0530 Subject: [PATCH 3/8] feat(subscription-service): update package lock update package lock BREAKING CHANGE: yes gh-34 --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index caa3c67..8cda42d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11991,7 +11991,7 @@ "node_modules/loopback4-billing": { "version": "0.0.1", "resolved": "file:services/subscription-service/loopback4-billing-0.0.1.tgz", - "integrity": "sha512-jQzEnJ/QcDwBKV2kcAzRKlrhfGZdTsMBOykmuLLk2649BfNnEQsP4Q+zy+Ceg92cYSD/wnj6dK31EeFG2DtxXQ==", + "integrity": "sha512-jgvBC5txeZ+xJONmUibVGXH2fiTr5Zh63yEcdVSBuuphl+SpXiVmKKqMwh7m1Y9sUVGJsqYnWCNxYPzIMmnf2Q==", "hasInstallScript": true, "license": "MIT", "dependencies": { From bce7f39b4fa418481248f3cd453bb4aebf0291ad Mon Sep 17 00:00:00 2001 From: Tyagi-Sunny Date: Tue, 24 Sep 2024 11:05:17 +0530 Subject: [PATCH 4/8] feat(subscription-service): update tar file of billing package update tar file of billing package gh-34 --- package-lock.json | 14 +++++++------- .../loopback4-billing-0.0.1.tgz | Bin 29504 -> 29581 bytes 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8cda42d..25ea57f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5440,9 +5440,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001662", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz", - "integrity": "sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==", + "version": "1.0.30001663", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz", + "integrity": "sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==", "dev": true, "funding": [ { @@ -7388,9 +7388,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.26", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.26.tgz", - "integrity": "sha512-Z+OMe9M/V6Ep9n/52+b7lkvYEps26z4Yz3vjWL1V61W0q+VLF1pOHhMY17sa4roz4AWmULSI8E6SAojZA5L0YQ==", + "version": "1.5.27", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz", + "integrity": "sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==", "dev": true }, "node_modules/emoji-regex": { @@ -11991,7 +11991,7 @@ "node_modules/loopback4-billing": { "version": "0.0.1", "resolved": "file:services/subscription-service/loopback4-billing-0.0.1.tgz", - "integrity": "sha512-jgvBC5txeZ+xJONmUibVGXH2fiTr5Zh63yEcdVSBuuphl+SpXiVmKKqMwh7m1Y9sUVGJsqYnWCNxYPzIMmnf2Q==", + "integrity": "sha512-RO0ja3BgaNvp/SCOwuSfcl3GYofpdC3RCxgin9f4KNwuh6PqSeWGbewJ6/f8aQ3cb6xj13AlwylO6OavWnt1OQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/services/subscription-service/loopback4-billing-0.0.1.tgz b/services/subscription-service/loopback4-billing-0.0.1.tgz index 5e14d75640ecd2af1d630c09b9e14a9f2c70605c..81152cf2ad6d402a11bd676df5e0e9974cb18213 100644 GIT binary patch delta 29152 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 delta 29018 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 From 5341fe154c681acc7037b6d033bb47ced92964ae Mon Sep 17 00:00:00 2001 From: Tyagi-Sunny Date: Tue, 24 Sep 2024 17:07:18 +0530 Subject: [PATCH 5/8] fix(tenant-management): add saastenantrepo for webhook handler add saastenantrepo for webhook handler gh-34 --- .../src/repositories/index.ts | 1 + .../repositories/saas-tenant.repository.ts | 89 +++++++++++++++++++ .../webhook/provisioning-webhook.handler.ts | 3 +- .../src/webhook.component.ts | 2 + 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 services/tenant-management-service/src/repositories/saas-tenant.repository.ts diff --git a/services/tenant-management-service/src/repositories/index.ts b/services/tenant-management-service/src/repositories/index.ts index 5c5157a..ea0ceba 100644 --- a/services/tenant-management-service/src/repositories/index.ts +++ b/services/tenant-management-service/src/repositories/index.ts @@ -6,3 +6,4 @@ export * from './resource.repository'; export * from './invoice.repository'; export * from './address.repository'; export * from './lead-token.repository'; +export * from './saas-tenant.repository'; \ No newline at end of file diff --git a/services/tenant-management-service/src/repositories/saas-tenant.repository.ts b/services/tenant-management-service/src/repositories/saas-tenant.repository.ts new file mode 100644 index 0000000..72b8ae5 --- /dev/null +++ b/services/tenant-management-service/src/repositories/saas-tenant.repository.ts @@ -0,0 +1,89 @@ +import {Getter, inject} from '@loopback/core'; +import { + BelongsToAccessor, + HasManyRepositoryFactory, + juggler, + repository, +} from '@loopback/repository'; +import { + DefaultTransactionalUserModifyRepository, + IAuthUserWithPermissions, +} from '@sourceloop/core'; +import {AuthenticationBindings} from 'loopback4-authentication'; + +import { + Address, + Contact, + Lead, + Resource, + Tenant, + TenantRelations, +} from '../models'; +import {ContactRepository} from './contact.repository'; +import {LeadRepository} from './lead.repository'; +import {ResourceRepository} from './resource.repository'; +import {AddressRepository} from './address.repository'; +import {TenantManagementDbSourceName} from '../types'; + +export class SaasTenantRepository extends DefaultTransactionalUserModifyRepository< + Tenant, + typeof Tenant.prototype.id, + TenantRelations +> { + public readonly contacts: HasManyRepositoryFactory< + Contact, + typeof Tenant.prototype.id + >; + + public readonly resources: HasManyRepositoryFactory< + Resource, + typeof Tenant.prototype.id + >; + + public readonly lead: BelongsToAccessor; + + public readonly address: BelongsToAccessor< + Address, + typeof Tenant.prototype.id + >; + + constructor( + @inject(`datasources.${TenantManagementDbSourceName}`) + dataSource: juggler.DataSource, + @inject.getter(AuthenticationBindings.CURRENT_USER) + public readonly getCurrentUser: Getter, + @repository.getter('ContactRepository') + protected contactRepositoryGetter: Getter, + @repository.getter('LeadRepository') + protected leadRepositoryGetter: Getter, + @repository.getter('ResourceRepository') + protected resourceRepositoryGetter: Getter, + @repository.getter('AddressRepository') + protected addressRepositoryGetter: Getter, + ) { + super(Tenant, dataSource, getCurrentUser); + this.lead = this.createBelongsToAccessorFor('lead', leadRepositoryGetter); + this.registerInclusionResolver('lead', this.lead.inclusionResolver); + this.contacts = this.createHasManyRepositoryFactoryFor( + 'contacts', + contactRepositoryGetter, + ); + this.registerInclusionResolver('contacts', this.contacts.inclusionResolver); + + this.resources = this.createHasManyRepositoryFactoryFor( + 'resources', + resourceRepositoryGetter, + ); + this.registerInclusionResolver( + 'resources', + this.resources.inclusionResolver, + ); + + this.address = this.createBelongsToAccessorFor( + 'address', + addressRepositoryGetter, + ); + this.registerInclusionResolver('address', this.address.inclusionResolver); + } +} + diff --git a/services/tenant-management-service/src/services/webhook/provisioning-webhook.handler.ts b/services/tenant-management-service/src/services/webhook/provisioning-webhook.handler.ts index 402b4f5..ce7e305 100644 --- a/services/tenant-management-service/src/services/webhook/provisioning-webhook.handler.ts +++ b/services/tenant-management-service/src/services/webhook/provisioning-webhook.handler.ts @@ -15,6 +15,7 @@ import { } from '../../types'; import {CryptoHelperService} from '../crypto-helper.service'; import {NotificationService} from '../notifications'; +import { SaasTenantRepository } from '../../repositories/saas-tenant.repository'; /** * Handler for provisioning webhooks. @@ -37,7 +38,7 @@ export class ProvisioningWebhookHandler implements IWebhookHandler { constructor( @repository(ResourceRepository) public resourceRepository: ResourceRepository, - @repository(TenantRepository) + @repository(SaasTenantRepository) public tenantRepository: TenantRepository, @inject('services.NotificationService') private notificationService: NotificationService, diff --git a/services/tenant-management-service/src/webhook.component.ts b/services/tenant-management-service/src/webhook.component.ts index 959e5f7..7dde2e4 100644 --- a/services/tenant-management-service/src/webhook.component.ts +++ b/services/tenant-management-service/src/webhook.component.ts @@ -60,6 +60,7 @@ import { ResourceRepository, TenantRepository, WebhookSecretRepository, + SaasTenantRepository, } from './repositories'; import {WebhookVerifierProvider} from './interceptors'; import {SystemUserProvider} from './providers'; @@ -109,6 +110,7 @@ export class WebhookTenantManagementServiceComponent implements Component { LeadRepository, ResourceRepository, TenantRepository, + SaasTenantRepository, WebhookSecretRepository, ]; From ae2ccbb9e836bf4b6c4f1d4cba556af7082522cc Mon Sep 17 00:00:00 2001 From: Tyagi-Sunny Date: Fri, 27 Sep 2024 17:32:22 +0530 Subject: [PATCH 6/8] feat(tenant-management): configure idp for user configure idp for user BREAKING CHANGE: yes 43 --- package-lock.json | 118 +++++++------- ...20240925102459-add-table-tenant-configs.js | 53 +++++++ ...25102459-add-table-tenant-configs-down.sql | 1 + ...0925102459-add-table-tenant-configs-up.sql | 17 ++ .../src/component.ts | 40 ++--- .../src/controllers/idp.controller.ts | 65 ++++++++ .../src/controllers/index.ts | 3 + .../tenant-config-tenant.controller.ts | 38 +++++ .../controllers/tenant-config.controller.ts | 150 ++++++++++++++++++ .../tenant-management-service/src/keys.ts | 26 ++- .../src/models/dtos/idp-details-dto.model.ts | 31 ++++ .../src/models/dtos/tenant-dto.model.ts | 32 ++++ .../src/models/index.ts | 1 + .../src/models/tenant-config.model.ts | 46 ++++++ .../providers/idp/idp-keycloak.provider.ts | 138 ++++++++++++++++ .../src/providers/idp/index.ts | 1 + .../src/providers/index.ts | 1 + .../src/repositories/index.ts | 1 + .../repositories/tenant-config.repository.ts | 27 ++++ .../src/types/i-idp.interface.ts | 15 ++ .../src/types/index.ts | 1 + .../src/webhook.component.ts | 13 +- 22 files changed, 730 insertions(+), 88 deletions(-) create mode 100644 services/tenant-management-service/migrations/pg/migrations/20240925102459-add-table-tenant-configs.js create mode 100644 services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-down.sql create mode 100644 services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-up.sql create mode 100644 services/tenant-management-service/src/controllers/idp.controller.ts create mode 100644 services/tenant-management-service/src/controllers/tenant-config-tenant.controller.ts create mode 100644 services/tenant-management-service/src/controllers/tenant-config.controller.ts create mode 100644 services/tenant-management-service/src/models/dtos/idp-details-dto.model.ts create mode 100644 services/tenant-management-service/src/models/dtos/tenant-dto.model.ts create mode 100644 services/tenant-management-service/src/models/tenant-config.model.ts create mode 100644 services/tenant-management-service/src/providers/idp/idp-keycloak.provider.ts create mode 100644 services/tenant-management-service/src/providers/idp/index.ts create mode 100644 services/tenant-management-service/src/repositories/tenant-config.repository.ts create mode 100644 services/tenant-management-service/src/types/i-idp.interface.ts diff --git a/package-lock.json b/package-lock.json index 25ea57f..091acf3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1582,9 +1582,9 @@ } }, "node_modules/@loopback/build/node_modules/@types/node": { - "version": "16.18.108", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.108.tgz", - "integrity": "sha512-fj42LD82fSv6yN9C6Q4dzS+hujHj+pTv0IpRR3kI20fnYeS0ytBpjFO9OjmDowSPPt4lNKN46JLaKbCyP+BW2A==", + "version": "16.18.111", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.111.tgz", + "integrity": "sha512-U1l6itlxU+vrJ9KyowQLKV9X+UuQBRhBF9v/XkGhAGgNHHRWzyY7FfTYRXt3vYOXPrd8UGlbYFK5HdneKCwXPQ==", "dev": true }, "node_modules/@loopback/build/node_modules/brace-expansion": { @@ -2993,9 +2993,9 @@ } }, "node_modules/@openapi-contrib/openapi-schema-to-json-schema/node_modules/@types/node": { - "version": "20.16.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", - "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", + "version": "20.16.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.9.tgz", + "integrity": "sha512-rkvIVJxsOfBejxK7I0FO5sa2WxFmJCzoDwcd88+fq/CUfynNywTo/1/T6hyFz22CyztsnLS9nVlHOnTI36RH5w==", "dependencies": { "undici-types": "~6.19.2" } @@ -3660,9 +3660,9 @@ "dev": true }, "node_modules/@sourceloop/core": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@sourceloop/core/-/core-14.1.1.tgz", - "integrity": "sha512-nc13D5UMbF1dU9qsLMkDupaQLsCOrNnT0angXTApIc5DmoB+7vwIHFBpm8Q7RHXh18+e7TSMmgPpFNSttpV6+w==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@sourceloop/core/-/core-14.1.2.tgz", + "integrity": "sha512-eZvDLXhRjHzx/FC7J2z3nkQU7Cj84meWZ62uyJYTwclQ+1l7S41x1mA03AM7K//F02yKfiU1x4BoYeY1JWeRaQ==", "dependencies": { "@loopback/boot": "^7.0.2", "@loopback/context": "^7.0.2", @@ -3717,9 +3717,9 @@ "link": true }, "node_modules/@sourceloop/feature-toggle-service": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sourceloop/feature-toggle-service/-/feature-toggle-service-3.0.0.tgz", - "integrity": "sha512-zmTO9T0jqwqXayXMhfXnhb71k6mJQysDbboMHuCQq+I31Jq33rzsq+I0op0PG+bw3yRh97yD6EaySrGWcsSn4A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sourceloop/feature-toggle-service/-/feature-toggle-service-3.0.1.tgz", + "integrity": "sha512-mq60cAE2csqJfOBmUKasoGLn6nC1Pwup/afBy+y34ScKxkeChVfvubz/0oE/0vCc90xFbZ7Ui399JRHSj621uw==", "hasInstallScript": true, "dependencies": { "@loopback/boot": "^7.0.2", @@ -3730,7 +3730,7 @@ "@loopback/rest": "^14.0.2", "@loopback/rest-explorer": "^7.0.2", "@loopback/service-proxy": "^7.0.2", - "@sourceloop/core": "^14.1.1", + "@sourceloop/core": "^14.1.2", "@types/proxyquire": "^1.3.28", "dotenv": "^16.0.3", "dotenv-extended": "^2.9.0", @@ -3869,9 +3869,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", - "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -3932,9 +3932,9 @@ } }, "node_modules/@types/lodash": { - "version": "4.17.7", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", - "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==" + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.9.tgz", + "integrity": "sha512-w9iWudx1XWOHW5lQRS9iKpK/XuRhnN+0T7HvdCCd802FYkT1AMTnxndJHGrNJwRoRHkslGr4S29tjm1cT7x/7w==" }, "node_modules/@types/methods": { "version": "1.1.4", @@ -3980,9 +3980,9 @@ "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" }, "node_modules/@types/node": { - "version": "22.5.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz", - "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==", + "version": "22.7.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.3.tgz", + "integrity": "sha512-qXKfhXXqGTyBskvWEzJZPUxSslAiLaB6JGP1ic/XTH9ctGgzdgYguuLP1C601aRTSDNlLb0jbKqXjZ48GNraSA==", "dependencies": { "undici-types": "~6.19.2" } @@ -5046,9 +5046,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", - "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "funding": [ { @@ -5065,8 +5065,8 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, @@ -5440,9 +5440,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001663", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz", - "integrity": "sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==", + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==", "dev": true, "funding": [ { @@ -5855,9 +5855,9 @@ } }, "node_modules/commitizen": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.0.tgz", - "integrity": "sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.1.tgz", + "integrity": "sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw==", "dev": true, "dependencies": { "cachedir": "2.3.0", @@ -7388,9 +7388,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.27", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz", - "integrity": "sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==", + "version": "1.5.29", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.29.tgz", + "integrity": "sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw==", "dev": true }, "node_modules/emoji-regex": { @@ -8151,9 +8151,9 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fast-uri": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", - "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz", + "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==" }, "node_modules/fastq": { "version": "1.17.1", @@ -13269,9 +13269,9 @@ } }, "node_modules/nise/node_modules/path-to-regexp": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.1.0.tgz", - "integrity": "sha512-Bqn3vc8CMHty6zuD+tG23s6v2kwxslHEhTj4eYaVKGIEB+YX/2wd0/rgXLFD9G9id9KCtbVy/3ZgmvZjpa0UdQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", "dev": true, "engines": { "node": ">=16" @@ -15147,9 +15147,9 @@ } }, "node_modules/package-json-from-dist": { - "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==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, "node_modules/pacote": { "version": "15.2.0", @@ -19339,9 +19339,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", "dev": true, "funding": [ { @@ -19358,8 +19358,8 @@ } ], "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.0" }, "bin": { "update-browserslist-db": "cli.js" @@ -20086,9 +20086,9 @@ } }, "services/orchestrator-service/node_modules/@types/node": { - "version": "18.19.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz", - "integrity": "sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==", + "version": "18.19.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.53.tgz", + "integrity": "sha512-GLxgUgHhDKO1Edw9Q0lvMbiO/IQXJwJlMaqxSGBXMpPy8uhkCs2iiPFaB2Q/gmobnFkckD3rqTBMVjXdwq+nKg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -20165,9 +20165,9 @@ } }, "services/subscription-service/node_modules/@types/node": { - "version": "18.19.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz", - "integrity": "sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==", + "version": "18.19.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.53.tgz", + "integrity": "sha512-GLxgUgHhDKO1Edw9Q0lvMbiO/IQXJwJlMaqxSGBXMpPy8uhkCs2iiPFaB2Q/gmobnFkckD3rqTBMVjXdwq+nKg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -20249,9 +20249,9 @@ } }, "services/tenant-management-service/node_modules/@types/node": { - "version": "18.19.50", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.50.tgz", - "integrity": "sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==", + "version": "18.19.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.53.tgz", + "integrity": "sha512-GLxgUgHhDKO1Edw9Q0lvMbiO/IQXJwJlMaqxSGBXMpPy8uhkCs2iiPFaB2Q/gmobnFkckD3rqTBMVjXdwq+nKg==", "dev": true, "dependencies": { "undici-types": "~5.26.4" diff --git a/services/tenant-management-service/migrations/pg/migrations/20240925102459-add-table-tenant-configs.js b/services/tenant-management-service/migrations/pg/migrations/20240925102459-add-table-tenant-configs.js new file mode 100644 index 0000000..2052c51 --- /dev/null +++ b/services/tenant-management-service/migrations/pg/migrations/20240925102459-add-table-tenant-configs.js @@ -0,0 +1,53 @@ +'use strict'; + +var dbm; +var type; +var seed; +var fs = require('fs'); +var path = require('path'); +var Promise; + +/** + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function(options, seedLink) { + dbm = options.dbmigrate; + type = dbm.dataType; + seed = seedLink; + Promise = options.Promise; +}; + +exports.up = function(db) { + var filePath = path.join(__dirname, 'sqls', '20240925102459-add-table-tenant-configs-up.sql'); + return new Promise( function( resolve, reject ) { + fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ + if (err) return reject(err); + console.log('received data: ' + data); + + resolve(data); + }); + }) + .then(function(data) { + return db.runSql(data); + }); +}; + +exports.down = function(db) { + var filePath = path.join(__dirname, 'sqls', '20240925102459-add-table-tenant-configs-down.sql'); + return new Promise( function( resolve, reject ) { + fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ + if (err) return reject(err); + console.log('received data: ' + data); + + resolve(data); + }); + }) + .then(function(data) { + return db.runSql(data); + }); +}; + +exports._meta = { + "version": 1 +}; diff --git a/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-down.sql b/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-down.sql new file mode 100644 index 0000000..3284c15 --- /dev/null +++ b/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-down.sql @@ -0,0 +1 @@ +drop table main.tenant_configs; \ No newline at end of file diff --git a/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-up.sql b/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-up.sql new file mode 100644 index 0000000..fa27218 --- /dev/null +++ b/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-up.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS main.tenant_configs +( + id uuid NOT NULL DEFAULT (md5(((random())::text || (clock_timestamp())::text)))::uuid, + config_key varchar(100) NOT NULL, + config_value jsonb NOT NULL, + created_on timestamptz DEFAULT CURRENT_TIMESTAMP, + modified_on timestamptz, + created_by uuid, + modified_by uuid, + deleted boolean DEFAULT FALSE NOT NULL, + deleted_by uuid, + deleted_on timestamptz, + tenant_id uuid NOT NULL, + CONSTRAINT pk_tenant_configs_id PRIMARY KEY (id), + CONSTRAINT fk_tenant_configs_tenants FOREIGN KEY (tenant_id) + REFERENCES main.tenants(id) +) \ No newline at end of file diff --git a/services/tenant-management-service/src/component.ts b/services/tenant-management-service/src/component.ts index 33fc487..404e149 100644 --- a/services/tenant-management-service/src/component.ts +++ b/services/tenant-management-service/src/component.ts @@ -14,8 +14,8 @@ import { ProviderMap, ServiceOrProviderClass, } from '@loopback/core'; -import {Class, Model, Repository} from '@loopback/repository'; -import {RestApplication} from '@loopback/rest'; +import { Class, Model, Repository } from '@loopback/repository'; +import { RestApplication } from '@loopback/rest'; import { BearerVerifierBindings, BearerVerifierComponent, @@ -25,53 +25,54 @@ import { SECURITY_SCHEME_SPEC, ServiceSequence, } from '@sourceloop/core'; -import {AuthenticationComponent} from 'loopback4-authentication'; +import { AuthenticationComponent } from 'loopback4-authentication'; import { AuthorizationBindings, AuthorizationComponent, } from 'loopback4-authorization'; -import { - EventConnectorBinding, - LEAD_TOKEN_VERIFIER, - SYSTEM_USER, - TenantManagementServiceBindings, -} from './keys'; -import {ITenantManagementServiceConfig} from './types'; -import {InvoiceController} from './controllers/invoice.controller'; import { ContactController, HomePageController, - LeadTenantController, LeadController, + LeadTenantController, PingController, TenantController, } from './controllers'; +import { InvoiceController } from './controllers/invoice.controller'; +import { + EventConnectorBinding, + LEAD_TOKEN_VERIFIER, + SYSTEM_USER, + TenantManagementServiceBindings, +} from './keys'; import { Address, Contact, + CreateLeadDTO, Invoice, Lead, LeadToken, + ProvisioningDTO, Resource, Tenant, - WebhookSecret, - CreateLeadDTO, - ProvisioningDTO, + TenantConfig, TenantOnboardDTO, VerifyLeadResponseDTO, WebhookDTO, + WebhookSecret, } from './models'; +import { LeadTokenVerifierProvider, SystemUserProvider } from './providers'; import { AddressRepository, ContactRepository, InvoiceRepository, - LeadTokenRepository, LeadRepository, + LeadTokenRepository, ResourceRepository, + TenantConfigRepository, TenantRepository, WebhookSecretRepository, } from './repositories'; -import {LeadTokenVerifierProvider, SystemUserProvider} from './providers'; import { CryptoHelperService, EventConnector, @@ -81,6 +82,7 @@ import { OnboardingService, ProvisioningService, } from './services'; +import { ITenantManagementServiceConfig } from './types'; export class TenantManagementServiceComponent implements Component { constructor( @inject(CoreBindings.APPLICATION_INSTANCE) @@ -120,6 +122,7 @@ export class TenantManagementServiceComponent implements Component { ResourceRepository, TenantRepository, WebhookSecretRepository, + TenantConfigRepository ]; this.models = [ @@ -136,6 +139,7 @@ export class TenantManagementServiceComponent implements Component { TenantOnboardDTO, VerifyLeadResponseDTO, WebhookDTO, + TenantConfig ]; this.controllers = [ @@ -145,7 +149,7 @@ export class TenantManagementServiceComponent implements Component { LeadTenantController, LeadController, PingController, - TenantController, + TenantController ]; this.bindings = [ diff --git a/services/tenant-management-service/src/controllers/idp.controller.ts b/services/tenant-management-service/src/controllers/idp.controller.ts new file mode 100644 index 0000000..086b51c --- /dev/null +++ b/services/tenant-management-service/src/controllers/idp.controller.ts @@ -0,0 +1,65 @@ +import { inject, intercept } from '@loopback/core'; +import { getModelSchemaRef, post, requestBody } from '@loopback/rest'; +import { + CONTENT_TYPE, + OPERATION_SECURITY_SPEC, + rateLimitKeyGenPublic, + STATUS_CODE, +} from '@sourceloop/core'; +import { authorize } from 'loopback4-authorization'; +import { ratelimit } from 'loopback4-ratelimiter'; +import { TenantManagementServiceBindings, WEBHOOK_VERIFIER } from '../keys'; +import { IdpDetailsDTO } from '../models/dtos/idp-details-dto.model'; +import { ConfigureIdpFunc, IdPKey } from '../types'; + +const basePath = '/manage/users'; +export class IdpController { + constructor( + @inject(TenantManagementServiceBindings.IDP_KEYCLOAK) + private readonly idpKeycloakProvider:ConfigureIdpFunc + ) { } + @intercept(WEBHOOK_VERIFIER) + @ratelimit(true, { + max: parseInt(process.env.WEBHOOK_API_MAX_ATTEMPTS ?? '10'), + keyGenerator: rateLimitKeyGenPublic, + }) + @authorize({ + permissions: ['*'], + }) + @post(`${basePath}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'Webhook success', + }, + }, + }) + async idpConfigure( + @requestBody({ + content: { + [CONTENT_TYPE.JSON]: { + schema: getModelSchemaRef(IdpDetailsDTO, { + title: 'IdpDetailsDTO', + }), + }, + }, + }) + payload: IdpDetailsDTO, + ): Promise { + switch (payload.identityProvider) { + case IdPKey.AUTH0: + + break; + case IdPKey.COGNITO: + + break; + case IdPKey.KEYCLOAK: + await this.idpKeycloakProvider(payload); + break; + + default: + break; + } + + } +} diff --git a/services/tenant-management-service/src/controllers/index.ts b/services/tenant-management-service/src/controllers/index.ts index 8406aab..f0b8847 100644 --- a/services/tenant-management-service/src/controllers/index.ts +++ b/services/tenant-management-service/src/controllers/index.ts @@ -6,3 +6,6 @@ export * from './lead-tenant.controller'; export * from './tenant.controller'; export * from './webhook.controller'; export * from './invoice.controller'; +export * from './tenant-config.controller'; +export * from './tenant-config-tenant.controller'; +export * from './idp.controller'; \ No newline at end of file diff --git a/services/tenant-management-service/src/controllers/tenant-config-tenant.controller.ts b/services/tenant-management-service/src/controllers/tenant-config-tenant.controller.ts new file mode 100644 index 0000000..fe79d87 --- /dev/null +++ b/services/tenant-management-service/src/controllers/tenant-config-tenant.controller.ts @@ -0,0 +1,38 @@ +import { + repository, +} from '@loopback/repository'; +import { + param, + get, + getModelSchemaRef, +} from '@loopback/rest'; +import { + TenantConfig, + Tenant, +} from '../models'; +import {TenantConfigRepository} from '../repositories'; + +export class TenantConfigTenantController { + constructor( + @repository(TenantConfigRepository) + public tenantConfigRepository: TenantConfigRepository, + ) { } + + @get('/tenant-configs/{id}/tenant', { + responses: { + '200': { + description: 'Tenant belonging to TenantConfig', + content: { + 'application/json': { + schema: getModelSchemaRef(Tenant), + }, + }, + }, + }, + }) + async getTenant( + @param.path.string('id') id: typeof TenantConfig.prototype.id, + ): Promise { + return this.tenantConfigRepository.tenant(id); + } +} diff --git a/services/tenant-management-service/src/controllers/tenant-config.controller.ts b/services/tenant-management-service/src/controllers/tenant-config.controller.ts new file mode 100644 index 0000000..b1bbcdf --- /dev/null +++ b/services/tenant-management-service/src/controllers/tenant-config.controller.ts @@ -0,0 +1,150 @@ +import { + Count, + CountSchema, + Filter, + FilterExcludingWhere, + repository, + Where, +} from '@loopback/repository'; +import { + post, + param, + get, + getModelSchemaRef, + patch, + put, + del, + requestBody, + response, +} from '@loopback/rest'; +import {TenantConfig} from '../models'; +import {TenantConfigRepository} from '../repositories'; + +export class TenantConfigController { + constructor( + @repository(TenantConfigRepository) + public tenantConfigRepository : TenantConfigRepository, + ) {} + + @post('/tenant-configs') + @response(200, { + description: 'TenantConfig model instance', + content: {'application/json': {schema: getModelSchemaRef(TenantConfig)}}, + }) + async create( + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(TenantConfig, { + title: 'NewTenantConfig', + exclude: ['id'], + }), + }, + }, + }) + tenantConfig: Omit, + ): Promise { + return this.tenantConfigRepository.create(tenantConfig); + } + + @get('/tenant-configs/count') + @response(200, { + description: 'TenantConfig model count', + content: {'application/json': {schema: CountSchema}}, + }) + async count( + @param.where(TenantConfig) where?: Where, + ): Promise { + return this.tenantConfigRepository.count(where); + } + + @get('/tenant-configs') + @response(200, { + description: 'Array of TenantConfig model instances', + content: { + 'application/json': { + schema: { + type: 'array', + items: getModelSchemaRef(TenantConfig, {includeRelations: true}), + }, + }, + }, + }) + async find( + @param.filter(TenantConfig) filter?: Filter, + ): Promise { + return this.tenantConfigRepository.find(filter); + } + + @patch('/tenant-configs') + @response(200, { + description: 'TenantConfig PATCH success count', + content: {'application/json': {schema: CountSchema}}, + }) + async updateAll( + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(TenantConfig, {partial: true}), + }, + }, + }) + tenantConfig: TenantConfig, + @param.where(TenantConfig) where?: Where, + ): Promise { + return this.tenantConfigRepository.updateAll(tenantConfig, where); + } + + @get('/tenant-configs/{id}') + @response(200, { + description: 'TenantConfig model instance', + content: { + 'application/json': { + schema: getModelSchemaRef(TenantConfig, {includeRelations: true}), + }, + }, + }) + async findById( + @param.path.string('id') id: string, + @param.filter(TenantConfig, {exclude: 'where'}) filter?: FilterExcludingWhere + ): Promise { + return this.tenantConfigRepository.findById(id, filter); + } + + @patch('/tenant-configs/{id}') + @response(204, { + description: 'TenantConfig PATCH success', + }) + async updateById( + @param.path.string('id') id: string, + @requestBody({ + content: { + 'application/json': { + schema: getModelSchemaRef(TenantConfig, {partial: true}), + }, + }, + }) + tenantConfig: TenantConfig, + ): Promise { + await this.tenantConfigRepository.updateById(id, tenantConfig); + } + + @put('/tenant-configs/{id}') + @response(204, { + description: 'TenantConfig PUT success', + }) + async replaceById( + @param.path.string('id') id: string, + @requestBody() tenantConfig: TenantConfig, + ): Promise { + await this.tenantConfigRepository.replaceById(id, tenantConfig); + } + + @del('/tenant-configs/{id}') + @response(204, { + description: 'TenantConfig DELETE success', + }) + async deleteById(@param.path.string('id') id: string): Promise { + await this.tenantConfigRepository.deleteById(id); + } +} diff --git a/services/tenant-management-service/src/keys.ts b/services/tenant-management-service/src/keys.ts index d9edad1..a1be917 100644 --- a/services/tenant-management-service/src/keys.ts +++ b/services/tenant-management-service/src/keys.ts @@ -1,27 +1,35 @@ -import {VerifyFunction} from 'loopback4-authentication'; +import { VerifyFunction } from 'loopback4-authentication'; import { + ConfigureIdpFunc, ITenantManagementServiceConfig, LeadUser, WebhookConfig, WebhookNotificationServiceType, } from './types'; -import {IAuthUser} from 'loopback4-authorization'; -import {AnyObject} from '@loopback/repository'; -import {WebhookController} from './controllers'; +import { IAuthUser } from 'loopback4-authorization'; +import { AnyObject } from '@loopback/repository'; +import { WebhookController } from './controllers'; import { BindingKey, BindingTemplate, Interceptor, extensionFor, } from '@loopback/core'; -import {BINDING_PREFIX} from '@sourceloop/core'; -import {IEventConnector} from './types/i-event-connector.interface'; +import { BINDING_PREFIX } from '@sourceloop/core'; +import { IEventConnector } from './types/i-event-connector.interface'; +import { ValueOrPromise } from '@loopback/context'; export namespace TenantManagementServiceBindings { export const Config = BindingKey.create( `${BINDING_PREFIX}.chat.config`, ); + /** + * Binding key for the Idp keycloak provider. + */ + export const IDP_KEYCLOAK = BindingKey.create< + ConfigureIdpFunc + >('sf.user.idp.keycloak'); } /** @@ -31,6 +39,10 @@ export const LEAD_TOKEN_VERIFIER = BindingKey.create< VerifyFunction.BearerFn >('sf.user.lead.verifier'); + + + + /** * Binding key for the system user. */ @@ -63,7 +75,7 @@ export const WebhookHandlerEP = BindingKey.create>( */ export const asWebhookHandler: BindingTemplate = binding => { extensionFor(WebhookHandlerEP.key)(binding); - binding.tag({namespace: WebhookHandlerEP.key}); + binding.tag({ namespace: WebhookHandlerEP.key }); }; export const WebhookNotificationService = diff --git a/services/tenant-management-service/src/models/dtos/idp-details-dto.model.ts b/services/tenant-management-service/src/models/dtos/idp-details-dto.model.ts new file mode 100644 index 0000000..c12cac6 --- /dev/null +++ b/services/tenant-management-service/src/models/dtos/idp-details-dto.model.ts @@ -0,0 +1,31 @@ +import { getJsonSchema } from '@loopback/openapi-v3'; +import { Model, model, property } from '@loopback/repository'; +import { IdpDetails, IdPKey } from '../../types'; +import { TenantDto } from './tenant-dto.model'; + +@model({ + description: 'model describing payload for IDP controller', +}) +export class IdpDetailsDTO extends Model implements IdpDetails { + @property({ + type: 'string', + description: 'identity provider - auth0 , keycloak , cognito', + required: true, + default: IdPKey.AUTH0, + jsonSchema: { + enum: Object.values(IdPKey), + }, + }) + identityProvider: IdPKey; + + @property({ + type: 'object', + description: 'address object to be created for the lead', + jsonSchema: getJsonSchema(TenantDto), + }) + tenant: TenantDto; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/tenant-management-service/src/models/dtos/tenant-dto.model.ts b/services/tenant-management-service/src/models/dtos/tenant-dto.model.ts new file mode 100644 index 0000000..3bd36ab --- /dev/null +++ b/services/tenant-management-service/src/models/dtos/tenant-dto.model.ts @@ -0,0 +1,32 @@ +import { getJsonSchema } from '@loopback/openapi-v3'; +import { model, property } from '@loopback/repository'; +import { Address } from '../address.model'; +import { Contact } from '../contact.model'; +import { Tenant } from '../tenant.model'; + +@model({ + description: 'model describing payload used to create a lead', +}) +export class TenantDto extends Tenant { + @property({ + type: 'object', + description: 'address object to be created for the lead', + jsonSchema: getJsonSchema(Address), + }) + address: Address; + + @property({ + type: 'array', + itemType: 'object', + description: 'Array of contact objects', + jsonSchema: { + type: 'object', + items: getJsonSchema(Contact), + }, + }) + contacts: Contact[]; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/tenant-management-service/src/models/index.ts b/services/tenant-management-service/src/models/index.ts index 02b5768..10cb796 100644 --- a/services/tenant-management-service/src/models/index.ts +++ b/services/tenant-management-service/src/models/index.ts @@ -7,3 +7,4 @@ export * from './resource.model'; export * from './invoice.model'; export * from './address.model'; export * from './lead-token.model'; +export * from './tenant-config.model'; \ No newline at end of file diff --git a/services/tenant-management-service/src/models/tenant-config.model.ts b/services/tenant-management-service/src/models/tenant-config.model.ts new file mode 100644 index 0000000..a700439 --- /dev/null +++ b/services/tenant-management-service/src/models/tenant-config.model.ts @@ -0,0 +1,46 @@ +import { belongsTo, model, property } from '@loopback/repository'; +import { UserModifiableEntity } from '@sourceloop/core'; +import { Tenant } from './tenant.model'; + +@model({ + name: 'tenant_configs', + description: 'tenant_configs to save any tenant specific data related to idP' +}) +export class TenantConfig extends UserModifiableEntity { + @property({ + type: 'string', + id: true, + generated: true, + }) + id: string; + + @property({ + type: 'string', + required: true, + name: 'config_key' + }) + configKey: string; + + @property({ + type: 'object', + required: true, + name: 'config_value' + }) + configValue: object; + + @belongsTo( + () => Tenant, + {keyTo: 'id'}, + { + type: 'string', + name: 'tenant_id', + description: 'id of the tenant this invoice is generated for', + required: true, + }, + ) + tenantId: string; + + constructor(data?: Partial) { + super(data); + } +} diff --git a/services/tenant-management-service/src/providers/idp/idp-keycloak.provider.ts b/services/tenant-management-service/src/providers/idp/idp-keycloak.provider.ts new file mode 100644 index 0000000..775752d --- /dev/null +++ b/services/tenant-management-service/src/providers/idp/idp-keycloak.provider.ts @@ -0,0 +1,138 @@ +import { Provider } from "@loopback/context"; +import axios from 'axios'; +import qs from 'qs'; +import { ConfigureIdpFunc, IdpDetails } from "../../types"; + +interface TokenResponse { + access_token: string; +} + +interface Credentials { + type: string; + value: string; + temporary: boolean; +} + +export class KeycloakIdpProvider implements Provider>{ + constructor(){} + + value(): ConfigureIdpFunc { + return (payload)=>this.configure(payload); + } + async configure(payload: IdpDetails): Promise { + const { tenant } = payload; + + try { + const token=await this.authenticateAdmin(); + // 1. Create a new realm using the tenant key + await this.createRealm(tenant.key,token); + + // 2. Create a new client within the realm + const clientId = `client-${tenant.key}`; // You can customize this as needed + await this.createClient(tenant.key, clientId,token); + + // 3. Create a new admin user for the tenant + const adminUsername = `${tenant.key}-admin`; // Customize this as needed + const adminPassword = 'your-secure-password'; // This can be dynamic or set in the environment + await this.createUser(tenant.key, adminUsername, adminPassword,token); + + console.log(`Successfully configured Keycloak for tenant: ${tenant.name}`); + } catch (error) { + console.error(`Error configuring Keycloak for tenant: ${tenant.name}`, error); + throw new Error(`Failed to configure Keycloak for tenant: ${tenant.name}`); + } + } + + + + + async authenticateAdmin(): Promise { + const response = await axios.post( + `${process.env.KEYCLOAK_HOST}/realms/master/protocol/openid-connect/token`, + qs.stringify({ + username: process.env.KEYCLOAK_ADMIN_USERNAME, + password: process.env.KEYCLOAK_ADMIN_PASSWORD, + grant_type: 'password', + client_id: 'admin-cli', + }), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + } + ); + + return response.data.access_token; + } + + async createRealm(realmName: string,token:string): Promise { + // const token = await this.authenticateAdmin(); + + const response = await axios.post( + `${process.env.KEYCLOAK_HOST}/admin/realms`, + { + realm: realmName, + enabled: true, + }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + + console.log('Realm created:', response.data); + } + + async createClient(realmName: string, clientId: string,token:string): Promise { + // const token = await this.authenticateAdmin(); + + const response = await axios.post( + `${process.env.KEYCLOAK_HOST}/admin/realms/${realmName}/clients`, + { + clientId: clientId, + publicClient: true, + directAccessGrantsEnabled: true, + protocol: 'openid-connect', + enabled: true, + }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + + console.log('Client created:', response.data); + } + + async createUser(realmName: string, username: string, password: string,token:string): Promise { + // const token = await this.authenticateAdmin(); + + const response = await axios.post( + `${process.env.KEYCLOAK_HOST}/admin/realms/${realmName}/users`, + { + username: username, + enabled: true, + credentials: [ + { + type: 'password', + value: password, + temporary: false, + }, + ] as Credentials[], + }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + + console.log('User created:', response.data); + } + + +} + + diff --git a/services/tenant-management-service/src/providers/idp/index.ts b/services/tenant-management-service/src/providers/idp/index.ts new file mode 100644 index 0000000..ff21cfb --- /dev/null +++ b/services/tenant-management-service/src/providers/idp/index.ts @@ -0,0 +1 @@ +export * from './idp-keycloak.provider' \ No newline at end of file diff --git a/services/tenant-management-service/src/providers/index.ts b/services/tenant-management-service/src/providers/index.ts index ec52e1f..d12ce03 100644 --- a/services/tenant-management-service/src/providers/index.ts +++ b/services/tenant-management-service/src/providers/index.ts @@ -1,2 +1,3 @@ export * from './lead-token-verify.provider'; export * from './system-user.provider'; +export * from './idp' \ No newline at end of file diff --git a/services/tenant-management-service/src/repositories/index.ts b/services/tenant-management-service/src/repositories/index.ts index ea0ceba..3e7105b 100644 --- a/services/tenant-management-service/src/repositories/index.ts +++ b/services/tenant-management-service/src/repositories/index.ts @@ -6,4 +6,5 @@ export * from './resource.repository'; export * from './invoice.repository'; export * from './address.repository'; export * from './lead-token.repository'; +export * from './tenant-config.repository'; export * from './saas-tenant.repository'; \ No newline at end of file diff --git a/services/tenant-management-service/src/repositories/tenant-config.repository.ts b/services/tenant-management-service/src/repositories/tenant-config.repository.ts new file mode 100644 index 0000000..67941ab --- /dev/null +++ b/services/tenant-management-service/src/repositories/tenant-config.repository.ts @@ -0,0 +1,27 @@ +import { Getter, inject } from '@loopback/core'; +import { BelongsToAccessor, juggler, repository } from '@loopback/repository'; +import { DefaultUserModifyCrudRepository, IAuthUserWithPermissions } from '@sourceloop/core'; +import { SYSTEM_USER } from '../keys'; +import { Tenant, TenantConfig } from '../models'; +import { TenantManagementDbSourceName } from '../types'; +import { TenantRepository } from './tenant.repository'; + +export class TenantConfigRepository extends DefaultUserModifyCrudRepository< + TenantConfig, + typeof TenantConfig.prototype.id, + {} +> { + + public readonly tenant: BelongsToAccessor; + + constructor( + @inject.getter(SYSTEM_USER) + public readonly getCurrentUser: Getter, + @inject(`datasources.${TenantManagementDbSourceName}`) + dataSource: juggler.DataSource, @repository.getter('TenantRepository') protected tenantRepositoryGetter: Getter, + ) { + super(TenantConfig, dataSource,getCurrentUser); + this.tenant = this.createBelongsToAccessorFor('tenant', tenantRepositoryGetter,); + this.registerInclusionResolver('tenant', this.tenant.inclusionResolver); + } +} diff --git a/services/tenant-management-service/src/types/i-idp.interface.ts b/services/tenant-management-service/src/types/i-idp.interface.ts new file mode 100644 index 0000000..1b0f3fe --- /dev/null +++ b/services/tenant-management-service/src/types/i-idp.interface.ts @@ -0,0 +1,15 @@ +import { Tenant } from "../models"; + +export enum IdPKey { + AUTH0 = 'auth0', + COGNITO = 'cognito', + KEYCLOAK = 'keycloak', + } + +export type ConfigureIdpFunc=(payload:IdpDetails)=>Promise; + + export interface IdpDetails { + identityProvider: IdPKey; + tenant: Tenant; + } + \ No newline at end of file diff --git a/services/tenant-management-service/src/types/index.ts b/services/tenant-management-service/src/types/index.ts index 9cea043..16f8603 100644 --- a/services/tenant-management-service/src/types/index.ts +++ b/services/tenant-management-service/src/types/index.ts @@ -38,3 +38,4 @@ export * from './resource.type'; export * from './i-provisioning-service.interface'; export * from './i-subscription.interface'; export * from './i-event-connector.interface'; +export * from './i-idp.interface'; \ No newline at end of file diff --git a/services/tenant-management-service/src/webhook.component.ts b/services/tenant-management-service/src/webhook.component.ts index 7dde2e4..39d5325 100644 --- a/services/tenant-management-service/src/webhook.component.ts +++ b/services/tenant-management-service/src/webhook.component.ts @@ -35,7 +35,7 @@ import { WEBHOOK_VERIFIER, } from './keys'; import {ITenantManagementServiceConfig} from './types'; -import {WebhookController} from './controllers'; +import {IdpController, TenantConfigController, TenantConfigTenantController, WebhookController} from './controllers'; import { Address, Contact, @@ -50,6 +50,7 @@ import { TenantOnboardDTO, VerifyLeadResponseDTO, WebhookDTO, + TenantConfig, } from './models'; import { AddressRepository, @@ -61,9 +62,10 @@ import { TenantRepository, WebhookSecretRepository, SaasTenantRepository, + TenantConfigRepository, } from './repositories'; import {WebhookVerifierProvider} from './interceptors'; -import {SystemUserProvider} from './providers'; +import {KeycloakIdpProvider, SystemUserProvider} from './providers'; import {CryptoHelperService, NotificationService} from './services'; import { DEFAULT_SIGNATURE_HEADER, @@ -112,6 +114,7 @@ export class WebhookTenantManagementServiceComponent implements Component { TenantRepository, SaasTenantRepository, WebhookSecretRepository, + TenantConfigRepository ]; this.models = [ @@ -128,12 +131,14 @@ export class WebhookTenantManagementServiceComponent implements Component { TenantOnboardDTO, VerifyLeadResponseDTO, WebhookDTO, + TenantConfig ]; - this.controllers = [WebhookController]; + this.controllers = [WebhookController,IdpController,TenantConfigController,TenantConfigTenantController]; this.bindings = [ - Binding.bind(WEBHOOK_VERIFIER).toProvider(WebhookVerifierProvider), + Binding.bind(WEBHOOK_VERIFIER).toProvider(WebhookVerifierProvider),Binding.bind(TenantManagementServiceBindings.IDP_KEYCLOAK).toProvider(KeycloakIdpProvider), + Binding.bind(SYSTEM_USER).toProvider(SystemUserProvider), Binding.bind(WEBHOOK_CONFIG).to({ signatureHeaderName: DEFAULT_SIGNATURE_HEADER, From f14113b71fec1de54b3da6d628981538dfa00c86 Mon Sep 17 00:00:00 2001 From: Tyagi-Sunny Date: Fri, 27 Sep 2024 17:57:31 +0530 Subject: [PATCH 7/8] feat(tenant-management): shgds sdhgs BREAKING CHANGE: yes 43 --- ...0925102459-add-table-tenant-configs-up.sql | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-up.sql b/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-up.sql index fa27218..9681a4c 100644 --- a/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-up.sql +++ b/services/tenant-management-service/migrations/pg/migrations/sqls/20240925102459-add-table-tenant-configs-up.sql @@ -3,8 +3,8 @@ CREATE TABLE IF NOT EXISTS main.tenant_configs id uuid NOT NULL DEFAULT (md5(((random())::text || (clock_timestamp())::text)))::uuid, config_key varchar(100) NOT NULL, config_value jsonb NOT NULL, - created_on timestamptz DEFAULT CURRENT_TIMESTAMP, - modified_on timestamptz, + created_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL, + modified_on timestamptz DEFAULT CURRENT_TIMESTAMP NOT NULL, created_by uuid, modified_by uuid, deleted boolean DEFAULT FALSE NOT NULL, @@ -14,4 +14,20 @@ CREATE TABLE IF NOT EXISTS main.tenant_configs CONSTRAINT pk_tenant_configs_id PRIMARY KEY (id), CONSTRAINT fk_tenant_configs_tenants FOREIGN KEY (tenant_id) REFERENCES main.tenants(id) -) \ No newline at end of file +); + + +CREATE OR REPLACE FUNCTION main.moddatetime() + RETURNS TRIGGER + LANGUAGE plpgsql + AS $function$ +BEGIN + NEW.modified_on = now(); + RETURN NEW; +END; +$function$; + +CREATE TRIGGER mdt_tenant_configs + BEFORE UPDATE ON main.tenant_configs + FOR EACH ROW + EXECUTE FUNCTION main.moddatetime('modified_on'); From 0ecf67f5b2f0f0a9cfaf71a4a093b2a24428817a Mon Sep 17 00:00:00 2001 From: Surbhi-sharma1 Date: Fri, 27 Sep 2024 18:03:29 +0530 Subject: [PATCH 8/8] feat(tenant-management): integrate auth0 integrate auth0 GH-47 --- package-lock.json | 44 +++++ ...20240925102459-add-table-tenant-configs.js | 40 ++-- .../tenant-management-service/package.json | 2 + .../src/component.ts | 25 ++- .../src/controllers/idp.controller.ts | 110 +++++------ .../tenant-config-tenant.controller.ts | 35 ++-- .../controllers/tenant-config.controller.ts | 183 +++++++++++++----- .../tenant-management-service/src/keys.ts | 35 ++-- .../src/models/dtos/idp-details-dto.model.ts | 12 +- .../src/models/dtos/tenant-dto.model.ts | 17 +- .../src/models/index.ts | 2 +- .../src/models/tenant-config.model.ts | 15 +- .../src/permissions.ts | 5 + .../src/providers/idp/idp-auth0.provider.ts | 164 ++++++++++++++++ .../providers/idp/idp-keycloak.provider.ts | 132 +++++++------ .../src/providers/idp/index.ts | 4 +- .../src/providers/idp/types.ts | 47 +++++ .../src/repositories/index.ts | 1 - .../repositories/tenant-config.repository.ts | 34 ++-- .../src/types/i-idp.interface.ts | 23 ++- .../src/types/index.ts | 2 +- .../src/webhook.component.ts | 29 ++- 22 files changed, 685 insertions(+), 276 deletions(-) create mode 100644 services/tenant-management-service/src/providers/idp/idp-auth0.provider.ts create mode 100644 services/tenant-management-service/src/providers/idp/types.ts diff --git a/package-lock.json b/package-lock.json index 091acf3..2508b31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3812,6 +3812,12 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/@types/auth0": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@types/auth0/-/auth0-3.3.10.tgz", + "integrity": "sha512-9tS0Y2igWxw+Dx5uCHkIUCu6tG0oRkwpE322dOJPwZMLXQMx49n/gDmUz7YJSe1iVjrWW+ffVYmlPShVIEwjkg==", + "dev": true + }, "node_modules/@types/aws-lambda": { "version": "8.10.145", "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.145.tgz", @@ -4788,6 +4794,33 @@ "node": ">= 4.0.0" } }, + "node_modules/auth0": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/auth0/-/auth0-4.10.0.tgz", + "integrity": "sha512-xfNtSyL84w9z1DQXWV1GXgtq2Oi3OXeJe/r+pI29GKZHpfgspNb4rFqp/CqI8zKVir6L3Iq2KZgE2rDHRDtxfA==", + "dev": true, + "dependencies": { + "jose": "^4.13.2", + "undici-types": "^6.15.0", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/auth0/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/auto-parse": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/auto-parse/-/auto-parse-1.8.0.tgz", @@ -10748,6 +10781,15 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/jpeg-exif": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", @@ -20234,10 +20276,12 @@ "@loopback/build": "^11.0.2", "@loopback/eslint-config": "^15.0.2", "@loopback/testlab": "^7.0.2", + "@types/auth0": "^3.3.10", "@types/jsonwebtoken": "^9.0.5", "@types/moment": "^2.13.0", "@types/node": "^18.11.9", "@types/pdfkit": "^0.13.4", + "auth0": "^4.10.0", "eslint": "^8.57.0", "nodemon": "^2.0.21", "nyc": "^15.1.0", diff --git a/services/tenant-management-service/migrations/pg/migrations/20240925102459-add-table-tenant-configs.js b/services/tenant-management-service/migrations/pg/migrations/20240925102459-add-table-tenant-configs.js index 2052c51..9e7d6e8 100644 --- a/services/tenant-management-service/migrations/pg/migrations/20240925102459-add-table-tenant-configs.js +++ b/services/tenant-management-service/migrations/pg/migrations/20240925102459-add-table-tenant-configs.js @@ -8,46 +8,52 @@ var path = require('path'); var Promise; /** - * We receive the dbmigrate dependency from dbmigrate initially. - * This enables us to not have to rely on NODE_PATH. - */ -exports.setup = function(options, seedLink) { + * We receive the dbmigrate dependency from dbmigrate initially. + * This enables us to not have to rely on NODE_PATH. + */ +exports.setup = function (options, seedLink) { dbm = options.dbmigrate; type = dbm.dataType; seed = seedLink; Promise = options.Promise; }; -exports.up = function(db) { - var filePath = path.join(__dirname, 'sqls', '20240925102459-add-table-tenant-configs-up.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.up = function (db) { + var filePath = path.join( + __dirname, + 'sqls', + '20240925102459-add-table-tenant-configs-up.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, {encoding: 'utf-8'}, function (err, data) { if (err) return reject(err); console.log('received data: ' + data); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; -exports.down = function(db) { - var filePath = path.join(__dirname, 'sqls', '20240925102459-add-table-tenant-configs-down.sql'); - return new Promise( function( resolve, reject ) { - fs.readFile(filePath, {encoding: 'utf-8'}, function(err,data){ +exports.down = function (db) { + var filePath = path.join( + __dirname, + 'sqls', + '20240925102459-add-table-tenant-configs-down.sql', + ); + return new Promise(function (resolve, reject) { + fs.readFile(filePath, {encoding: 'utf-8'}, function (err, data) { if (err) return reject(err); console.log('received data: ' + data); resolve(data); }); - }) - .then(function(data) { + }).then(function (data) { return db.runSql(data); }); }; exports._meta = { - "version": 1 + version: 1, }; diff --git a/services/tenant-management-service/package.json b/services/tenant-management-service/package.json index 62c7912..335b615 100644 --- a/services/tenant-management-service/package.json +++ b/services/tenant-management-service/package.json @@ -101,10 +101,12 @@ "@loopback/build": "^11.0.2", "@loopback/eslint-config": "^15.0.2", "@loopback/testlab": "^7.0.2", + "@types/auth0": "^3.3.10", "@types/jsonwebtoken": "^9.0.5", "@types/moment": "^2.13.0", "@types/node": "^18.11.9", "@types/pdfkit": "^0.13.4", + "auth0": "^4.10.0", "eslint": "^8.57.0", "nodemon": "^2.0.21", "nyc": "^15.1.0", diff --git a/services/tenant-management-service/src/component.ts b/services/tenant-management-service/src/component.ts index 404e149..17e53cd 100644 --- a/services/tenant-management-service/src/component.ts +++ b/services/tenant-management-service/src/component.ts @@ -45,6 +45,18 @@ import { SYSTEM_USER, TenantManagementServiceBindings, } from './keys'; +import {ITenantManagementServiceConfig} from './types'; +import {InvoiceController} from './controllers/invoice.controller'; +import { + ContactController, + HomePageController, + LeadTenantController, + LeadController, + PingController, + TenantController, + TenantConfigController, + TenantConfigTenantController, +} from './controllers'; import { Address, Contact, @@ -82,7 +94,8 @@ import { OnboardingService, ProvisioningService, } from './services'; -import { ITenantManagementServiceConfig } from './types'; +import {IdpController} from './controllers/idp.controller'; + export class TenantManagementServiceComponent implements Component { constructor( @inject(CoreBindings.APPLICATION_INSTANCE) @@ -122,7 +135,7 @@ export class TenantManagementServiceComponent implements Component { ResourceRepository, TenantRepository, WebhookSecretRepository, - TenantConfigRepository + TenantConfigRepository, ]; this.models = [ @@ -139,7 +152,7 @@ export class TenantManagementServiceComponent implements Component { TenantOnboardDTO, VerifyLeadResponseDTO, WebhookDTO, - TenantConfig + TenantConfig, ]; this.controllers = [ @@ -149,12 +162,16 @@ export class TenantManagementServiceComponent implements Component { LeadTenantController, LeadController, PingController, - TenantController + TenantController, + IdpController, + TenantConfigController, + TenantConfigTenantController, ]; this.bindings = [ Binding.bind(LEAD_TOKEN_VERIFIER).toProvider(LeadTokenVerifierProvider), Binding.bind(SYSTEM_USER).toProvider(SystemUserProvider), + createServiceBinding(ProvisioningService), createServiceBinding(OnboardingService), createServiceBinding(LeadAuthenticator), diff --git a/services/tenant-management-service/src/controllers/idp.controller.ts b/services/tenant-management-service/src/controllers/idp.controller.ts index 086b51c..15ee0e5 100644 --- a/services/tenant-management-service/src/controllers/idp.controller.ts +++ b/services/tenant-management-service/src/controllers/idp.controller.ts @@ -1,65 +1,65 @@ -import { inject, intercept } from '@loopback/core'; -import { getModelSchemaRef, post, requestBody } from '@loopback/rest'; +import {inject, intercept} from '@loopback/core'; +import {getModelSchemaRef, post, requestBody} from '@loopback/rest'; import { - CONTENT_TYPE, - OPERATION_SECURITY_SPEC, - rateLimitKeyGenPublic, - STATUS_CODE, + CONTENT_TYPE, + OPERATION_SECURITY_SPEC, + rateLimitKeyGenPublic, + STATUS_CODE, } from '@sourceloop/core'; -import { authorize } from 'loopback4-authorization'; -import { ratelimit } from 'loopback4-ratelimiter'; -import { TenantManagementServiceBindings, WEBHOOK_VERIFIER } from '../keys'; -import { IdpDetailsDTO } from '../models/dtos/idp-details-dto.model'; -import { ConfigureIdpFunc, IdPKey } from '../types'; +import {authorize} from 'loopback4-authorization'; +import {ratelimit} from 'loopback4-ratelimiter'; +import {TenantManagementServiceBindings, WEBHOOK_VERIFIER} from '../keys'; +import {IdpDetailsDTO} from '../models/dtos/idp-details-dto.model'; +import {ConfigureIdpFunc, IdPKey} from '../types'; const basePath = '/manage/users'; export class IdpController { - constructor( - @inject(TenantManagementServiceBindings.IDP_KEYCLOAK) - private readonly idpKeycloakProvider:ConfigureIdpFunc - ) { } - @intercept(WEBHOOK_VERIFIER) - @ratelimit(true, { - max: parseInt(process.env.WEBHOOK_API_MAX_ATTEMPTS ?? '10'), - keyGenerator: rateLimitKeyGenPublic, - }) - @authorize({ - permissions: ['*'], - }) - @post(`${basePath}`, { - security: OPERATION_SECURITY_SPEC, - responses: { - [STATUS_CODE.NO_CONTENT]: { - description: 'Webhook success', - }, + constructor( + @inject(TenantManagementServiceBindings.IDP_KEYCLOAK) + private readonly idpKeycloakProvider: ConfigureIdpFunc, + @inject(TenantManagementServiceBindings.IDP_AUTH0) + private readonly idpAuth0Provider: ConfigureIdpFunc, + ) {} + @intercept(WEBHOOK_VERIFIER) + @ratelimit(true, { + max: parseInt(process.env.WEBHOOK_API_MAX_ATTEMPTS ?? '10'), + keyGenerator: rateLimitKeyGenPublic, + }) + @authorize({ + permissions: ['*'], + }) + @post(`${basePath}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'Webhook success', + }, + }, + }) + async idpConfigure( + @requestBody({ + content: { + [CONTENT_TYPE.JSON]: { + schema: getModelSchemaRef(IdpDetailsDTO, { + title: 'IdpDetailsDTO', + }), }, + }, }) - async idpConfigure( - @requestBody({ - content: { - [CONTENT_TYPE.JSON]: { - schema: getModelSchemaRef(IdpDetailsDTO, { - title: 'IdpDetailsDTO', - }), - }, - }, - }) - payload: IdpDetailsDTO, - ): Promise { - switch (payload.identityProvider) { - case IdPKey.AUTH0: - - break; - case IdPKey.COGNITO: - - break; - case IdPKey.KEYCLOAK: - await this.idpKeycloakProvider(payload); - break; - - default: - break; - } + payload: IdpDetailsDTO, + ): Promise { + switch (payload.identityProvider) { + case IdPKey.AUTH0: + await this.idpAuth0Provider(payload); + break; + case IdPKey.COGNITO: + break; + case IdPKey.KEYCLOAK: + await this.idpKeycloakProvider(payload); + break; + default: + break; } + } } diff --git a/services/tenant-management-service/src/controllers/tenant-config-tenant.controller.ts b/services/tenant-management-service/src/controllers/tenant-config-tenant.controller.ts index fe79d87..5a3e99e 100644 --- a/services/tenant-management-service/src/controllers/tenant-config-tenant.controller.ts +++ b/services/tenant-management-service/src/controllers/tenant-config-tenant.controller.ts @@ -1,26 +1,27 @@ -import { - repository, -} from '@loopback/repository'; -import { - param, - get, - getModelSchemaRef, -} from '@loopback/rest'; -import { - TenantConfig, - Tenant, -} from '../models'; +import {repository} from '@loopback/repository'; +import {param, get, getModelSchemaRef} from '@loopback/rest'; +import {TenantConfig, Tenant} from '../models'; import {TenantConfigRepository} from '../repositories'; - +import {authenticate, STRATEGY} from 'loopback4-authentication'; +import {authorize} from 'loopback4-authorization'; +import {PermissionKey} from '../permissions'; +import {OPERATION_SECURITY_SPEC, STATUS_CODE} from '@sourceloop/core'; +const basePath = '/tenant-configs/{id}/tenant'; export class TenantConfigTenantController { constructor( @repository(TenantConfigRepository) public tenantConfigRepository: TenantConfigRepository, - ) { } - - @get('/tenant-configs/{id}/tenant', { + ) {} + @authorize({ + permissions: [PermissionKey.ViewTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @get(`${basePath}`, { + security: OPERATION_SECURITY_SPEC, responses: { - '200': { + [STATUS_CODE.OK]: { description: 'Tenant belonging to TenantConfig', content: { 'application/json': { diff --git a/services/tenant-management-service/src/controllers/tenant-config.controller.ts b/services/tenant-management-service/src/controllers/tenant-config.controller.ts index b1bbcdf..fc690cc 100644 --- a/services/tenant-management-service/src/controllers/tenant-config.controller.ts +++ b/services/tenant-management-service/src/controllers/tenant-config.controller.ts @@ -15,21 +15,39 @@ import { put, del, requestBody, - response, } from '@loopback/rest'; import {TenantConfig} from '../models'; import {TenantConfigRepository} from '../repositories'; - +import {authenticate, STRATEGY} from 'loopback4-authentication'; +import {authorize} from 'loopback4-authorization'; +import {PermissionKey} from '../permissions'; +import { + CONTENT_TYPE, + OPERATION_SECURITY_SPEC, + STATUS_CODE, +} from '@sourceloop/core'; +const basePath = '/tenant-configs'; export class TenantConfigController { constructor( @repository(TenantConfigRepository) - public tenantConfigRepository : TenantConfigRepository, + public tenantConfigRepository: TenantConfigRepository, ) {} - - @post('/tenant-configs') - @response(200, { - description: 'TenantConfig model instance', - content: {'application/json': {schema: getModelSchemaRef(TenantConfig)}}, + @authorize({ + permissions: [PermissionKey.CreateTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @post(basePath, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'Tenant Config model instance', + content: { + [CONTENT_TYPE.JSON]: {schema: getModelSchemaRef(TenantConfig)}, + }, + }, + }, }) async create( @requestBody({ @@ -46,26 +64,44 @@ export class TenantConfigController { ): Promise { return this.tenantConfigRepository.create(tenantConfig); } - - @get('/tenant-configs/count') - @response(200, { - description: 'TenantConfig model count', - content: {'application/json': {schema: CountSchema}}, + @authorize({ + permissions: [PermissionKey.ViewTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @get(`${basePath}/count`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'Tenant Config model count', + content: {[CONTENT_TYPE.JSON]: {schema: CountSchema}}, + }, + }, }) async count( @param.where(TenantConfig) where?: Where, ): Promise { return this.tenantConfigRepository.count(where); } - - @get('/tenant-configs') - @response(200, { - description: 'Array of TenantConfig model instances', - content: { - 'application/json': { - schema: { - type: 'array', - items: getModelSchemaRef(TenantConfig, {includeRelations: true}), + @authorize({ + permissions: [PermissionKey.ViewTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @get(basePath, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'Array of TenantConfig model instances', + content: { + [CONTENT_TYPE.JSON]: { + schema: { + type: 'array', + items: getModelSchemaRef(TenantConfig, {includeRelations: true}), + }, + }, }, }, }, @@ -75,11 +111,24 @@ export class TenantConfigController { ): Promise { return this.tenantConfigRepository.find(filter); } - - @patch('/tenant-configs') - @response(200, { - description: 'TenantConfig PATCH success count', - content: {'application/json': {schema: CountSchema}}, + @authorize({ + permissions: [PermissionKey.UpdateTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @patch(basePath, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'Tenant Config PATCH success', + content: { + [CONTENT_TYPE.JSON]: { + schema: getModelSchemaRef(TenantConfig), + }, + }, + }, + }, }) async updateAll( @requestBody({ @@ -94,26 +143,48 @@ export class TenantConfigController { ): Promise { return this.tenantConfigRepository.updateAll(tenantConfig, where); } - - @get('/tenant-configs/{id}') - @response(200, { - description: 'TenantConfig model instance', - content: { - 'application/json': { - schema: getModelSchemaRef(TenantConfig, {includeRelations: true}), + @authorize({ + permissions: [PermissionKey.ViewTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @get(`${basePath}/{id}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.OK]: { + description: 'Tenant Config model instance', + content: { + [CONTENT_TYPE.JSON]: {schema: getModelSchemaRef(TenantConfig)}, + }, }, }, }) async findById( @param.path.string('id') id: string, - @param.filter(TenantConfig, {exclude: 'where'}) filter?: FilterExcludingWhere + @param.filter(TenantConfig, {exclude: 'where'}) + filter?: FilterExcludingWhere, ): Promise { return this.tenantConfigRepository.findById(id, filter); } - - @patch('/tenant-configs/{id}') - @response(204, { - description: 'TenantConfig PATCH success', + @authorize({ + permissions: [PermissionKey.UpdateTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @patch(`${basePath}/{id}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'Tenant Config PATCH success', + content: { + [CONTENT_TYPE.JSON]: { + schema: getModelSchemaRef(TenantConfig), + }, + }, + }, + }, }) async updateById( @param.path.string('id') id: string, @@ -128,10 +199,19 @@ export class TenantConfigController { ): Promise { await this.tenantConfigRepository.updateById(id, tenantConfig); } - - @put('/tenant-configs/{id}') - @response(204, { - description: 'TenantConfig PUT success', + @authorize({ + permissions: [PermissionKey.UpdateTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @put(`${basePath}/{id}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'Tenant Config PUT success', + }, + }, }) async replaceById( @param.path.string('id') id: string, @@ -139,10 +219,19 @@ export class TenantConfigController { ): Promise { await this.tenantConfigRepository.replaceById(id, tenantConfig); } - - @del('/tenant-configs/{id}') - @response(204, { - description: 'TenantConfig DELETE success', + @authorize({ + permissions: [PermissionKey.DeleteTenantConfig], + }) + @authenticate(STRATEGY.BEARER, { + passReqToCallback: true, + }) + @del(`${basePath}/{id}`, { + security: OPERATION_SECURITY_SPEC, + responses: { + [STATUS_CODE.NO_CONTENT]: { + description: 'Tenant DELETE success', + }, + }, }) async deleteById(@param.path.string('id') id: string): Promise { await this.tenantConfigRepository.deleteById(id); diff --git a/services/tenant-management-service/src/keys.ts b/services/tenant-management-service/src/keys.ts index a1be917..a2f676a 100644 --- a/services/tenant-management-service/src/keys.ts +++ b/services/tenant-management-service/src/keys.ts @@ -1,4 +1,4 @@ -import { VerifyFunction } from 'loopback4-authentication'; +import {VerifyFunction} from 'loopback4-authentication'; import { ConfigureIdpFunc, ITenantManagementServiceConfig, @@ -6,18 +6,18 @@ import { WebhookConfig, WebhookNotificationServiceType, } from './types'; -import { IAuthUser } from 'loopback4-authorization'; -import { AnyObject } from '@loopback/repository'; -import { WebhookController } from './controllers'; +import {IAuthUser} from 'loopback4-authorization'; +import {AnyObject} from '@loopback/repository'; +import {WebhookController} from './controllers'; import { BindingKey, BindingTemplate, Interceptor, extensionFor, } from '@loopback/core'; -import { BINDING_PREFIX } from '@sourceloop/core'; -import { IEventConnector } from './types/i-event-connector.interface'; -import { ValueOrPromise } from '@loopback/context'; +import {BINDING_PREFIX} from '@sourceloop/core'; +import {IEventConnector} from './types/i-event-connector.interface'; +import {Auth0Response} from './providers/idp'; export namespace TenantManagementServiceBindings { export const Config = @@ -25,11 +25,16 @@ export namespace TenantManagementServiceBindings { `${BINDING_PREFIX}.chat.config`, ); /** - * Binding key for the Idp keycloak provider. - */ - export const IDP_KEYCLOAK = BindingKey.create< - ConfigureIdpFunc - >('sf.user.idp.keycloak'); + * Binding key for the Idp keycloak provider. + */ + export const IDP_KEYCLOAK = BindingKey.create>( + 'sf.user.idp.keycloak', + ); + /** + * Binding key for the Idp Auth0 provider. + */ + export const IDP_AUTH0 = + BindingKey.create>('sf.user.idp.auth0'); } /** @@ -39,10 +44,6 @@ export const LEAD_TOKEN_VERIFIER = BindingKey.create< VerifyFunction.BearerFn >('sf.user.lead.verifier'); - - - - /** * Binding key for the system user. */ @@ -75,7 +76,7 @@ export const WebhookHandlerEP = BindingKey.create>( */ export const asWebhookHandler: BindingTemplate = binding => { extensionFor(WebhookHandlerEP.key)(binding); - binding.tag({ namespace: WebhookHandlerEP.key }); + binding.tag({namespace: WebhookHandlerEP.key}); }; export const WebhookNotificationService = diff --git a/services/tenant-management-service/src/models/dtos/idp-details-dto.model.ts b/services/tenant-management-service/src/models/dtos/idp-details-dto.model.ts index c12cac6..6f606d1 100644 --- a/services/tenant-management-service/src/models/dtos/idp-details-dto.model.ts +++ b/services/tenant-management-service/src/models/dtos/idp-details-dto.model.ts @@ -1,7 +1,7 @@ -import { getJsonSchema } from '@loopback/openapi-v3'; -import { Model, model, property } from '@loopback/repository'; -import { IdpDetails, IdPKey } from '../../types'; -import { TenantDto } from './tenant-dto.model'; +import {getJsonSchema} from '@loopback/openapi-v3'; +import {Model, model, property} from '@loopback/repository'; +import {IdpDetails, IdPKey} from '../../types'; +import {TenantDto} from './tenant-dto.model'; @model({ description: 'model describing payload for IDP controller', @@ -13,8 +13,8 @@ export class IdpDetailsDTO extends Model implements IdpDetails { required: true, default: IdPKey.AUTH0, jsonSchema: { - enum: Object.values(IdPKey), - }, + enum: Object.values(IdPKey), + }, }) identityProvider: IdPKey; diff --git a/services/tenant-management-service/src/models/dtos/tenant-dto.model.ts b/services/tenant-management-service/src/models/dtos/tenant-dto.model.ts index 3bd36ab..c49b673 100644 --- a/services/tenant-management-service/src/models/dtos/tenant-dto.model.ts +++ b/services/tenant-management-service/src/models/dtos/tenant-dto.model.ts @@ -1,8 +1,9 @@ -import { getJsonSchema } from '@loopback/openapi-v3'; -import { model, property } from '@loopback/repository'; -import { Address } from '../address.model'; -import { Contact } from '../contact.model'; -import { Tenant } from '../tenant.model'; +import {getJsonSchema} from '@loopback/openapi-v3'; +import {AnyObject, model, property} from '@loopback/repository'; +import {Address} from '../address.model'; + +import {Tenant} from '../tenant.model'; +import {Contact} from '../contact.model'; @model({ description: 'model describing payload used to create a lead', @@ -25,6 +26,12 @@ export class TenantDto extends Tenant { }, }) contacts: Contact[]; + @property({ + type: 'object', + description: 'plan details', + jsonSchema: getJsonSchema(Object), + }) + plan: AnyObject; constructor(data?: Partial) { super(data); diff --git a/services/tenant-management-service/src/models/index.ts b/services/tenant-management-service/src/models/index.ts index 10cb796..d463d4d 100644 --- a/services/tenant-management-service/src/models/index.ts +++ b/services/tenant-management-service/src/models/index.ts @@ -7,4 +7,4 @@ export * from './resource.model'; export * from './invoice.model'; export * from './address.model'; export * from './lead-token.model'; -export * from './tenant-config.model'; \ No newline at end of file +export * from './tenant-config.model'; diff --git a/services/tenant-management-service/src/models/tenant-config.model.ts b/services/tenant-management-service/src/models/tenant-config.model.ts index a700439..17c5945 100644 --- a/services/tenant-management-service/src/models/tenant-config.model.ts +++ b/services/tenant-management-service/src/models/tenant-config.model.ts @@ -1,10 +1,11 @@ -import { belongsTo, model, property } from '@loopback/repository'; -import { UserModifiableEntity } from '@sourceloop/core'; -import { Tenant } from './tenant.model'; +import {model, property, belongsTo} from '@loopback/repository'; +import {UserModifiableEntity} from '@sourceloop/core'; +import {Tenant} from './tenant.model'; +import {ConfigValue} from '../providers/idp'; @model({ name: 'tenant_configs', - description: 'tenant_configs to save any tenant specific data related to idP' + description: 'tenant_configs to save any tenant specific data related to idP', }) export class TenantConfig extends UserModifiableEntity { @property({ @@ -17,16 +18,16 @@ export class TenantConfig extends UserModifiableEntity { @property({ type: 'string', required: true, - name: 'config_key' + name: 'config_key', }) configKey: string; @property({ type: 'object', required: true, - name: 'config_value' + name: 'config_value', }) - configValue: object; + configValue: ConfigValue; @belongsTo( () => Tenant, diff --git a/services/tenant-management-service/src/permissions.ts b/services/tenant-management-service/src/permissions.ts index 4cf7dd9..c4953c2 100644 --- a/services/tenant-management-service/src/permissions.ts +++ b/services/tenant-management-service/src/permissions.ts @@ -17,6 +17,11 @@ export const PermissionKey = { DeleteInvoice: '10214', ViewInvoice: '10215', CreateNotification: '2', + CreateTenantConfig: '10216', + + UpdateTenantConfig: '10217', + DeleteTenantConfig: '10218', + ViewTenantConfig: '10219', CreateSubscription: '7001', UpdateSubscription: '7002', diff --git a/services/tenant-management-service/src/providers/idp/idp-auth0.provider.ts b/services/tenant-management-service/src/providers/idp/idp-auth0.provider.ts new file mode 100644 index 0000000..b9219c1 --- /dev/null +++ b/services/tenant-management-service/src/providers/idp/idp-auth0.provider.ts @@ -0,0 +1,164 @@ +import {Provider} from '@loopback/context'; + +import {ConfigureIdpFunc, IdpDetails} from '../../types'; +import {ManagementClient} from 'auth0'; + +import {Auth0Response, ConfigValue, OrganizationData, UserData} from './types'; + +import {TenantConfigRepository} from '../../repositories/tenant-config.repository'; +import {repository} from '@loopback/repository'; + +import {HttpErrors} from '@loopback/rest'; +const STATUS_OK = 200; +const STATUS_NOT_FOUND = 404; +export class Auth0IdpProvider + implements Provider> +{ + management: ManagementClient; + + constructor( + @repository(TenantConfigRepository) + private readonly tenantConfigRepository: TenantConfigRepository, + ) {} + + value(): ConfigureIdpFunc { + return payload => this.configure(payload); + } + async configure(payload: IdpDetails): Promise { + this.management = new ManagementClient({ + domain: process.env.AUTH0_DOMAIN ?? '', + clientId: process.env.AUTH0_CLIENT_ID ?? '', + clientSecret: process.env.AUTH0_CLIENT_SECRET ?? '', + audience: process.env.AUTH0_AUDIENCE, + }); + const {tenant} = payload; + const planTier = tenant.plan.tier; + const tenantConfig = await this.tenantConfigRepository.findOne({ + where: {tenantId: tenant.id}, + }); + if (!tenantConfig) { + throw new HttpErrors.NotFound( + `Tenant configuration not found for tenant: ${tenant.id}`, + ); + } + const configValue: ConfigValue = tenantConfig.configValue; + const organizationData: OrganizationData = { + name: tenant.name, + // eslint-disable-next-line + display_name: configValue.display_name, + // eslint-disable-next-line + logo_url: configValue.logo_url, + // eslint-disable-next-line + primary_color: configValue.primary_color, + // eslint-disable-next-line + page_background: configValue.page_background, + // eslint-disable-next-line + link_color: configValue.link_color, + }; + + const userData: UserData = { + email: tenant.contacts[0].email, + + connection: configValue.connection, + password: configValue.password, + // eslint-disable-next-line + verify_email: configValue.verify_email, + // eslint-disable-next-line + phone_number: configValue.phone_number, + // eslint-disable-next-line + user_metadata: configValue.user_metadata, + blocked: configValue.blocked, + // eslint-disable-next-line + email_verified: configValue.email_verified, + // eslint-disable-next-line + app_metadata: configValue.app_metadata, + // eslint-disable-next-line + given_name: configValue.given_name, + // eslint-disable-next-line + family_name: configValue.family_name, + nickname: configValue.nickname, + picture: configValue.picture, + // eslint-disable-next-line + user_id: configValue.user_id, + }; + + let organizationId!: string; + + if (planTier === 'PREMIUM') { + const organization = await this.createOrganization(organizationData); + organizationId = organization.data.id; + } else { + try { + const organizationResponse = + await this.management.organizations.getByName({name: tenant.name}); + + if (organizationResponse.status === STATUS_OK) { + organizationId = organizationResponse.data.id; + } + } catch (error) { + if (error.statusCode === STATUS_NOT_FOUND) { + const organization = await this.createOrganization(organizationData); + organizationId = organization.data.id; + } else { + throw new Error(`Error checking organization: ${error.message}`); + } + } + } + + if (!organizationId) { + throw new Error('Failed to retrieve or create organization ID.'); + } + + const user = await this.createUser(userData); + const userId = user.data.user_id; + + await this.addMemberToOrganization(organizationId, userId); + return { + organizationId: organizationId, + userId: userId, + }; + } + async createOrganization(data: OrganizationData) { + try { + return await this.management.organizations.create({ + name: data.name, + // eslint-disable-next-line + display_name: data.display_name, + branding: { + // eslint-disable-next-line + logo_url: data.logo_url, + colors: { + primary: data.primary_color ?? '#007BFF', + // eslint-disable-next-line + page_background: data.page_background ?? '#007BFF', + }, + }, + // eslint-disable-next-line + enabled_connections: [], + }); + } catch (error) { + throw new Error(`Error creating organization: ${error.message}`); + } + } + async createUser(userData: UserData) { + try { + return await this.management.users.create(userData); + } catch (error) { + throw new Error(`Error creating user: ${error.message}`); + } + } + + async addMemberToOrganization(organizationId: string, userId: string) { + try { + return await this.management.organizations.addMembers( + {id: organizationId}, + { + members: [userId], + }, + ); + } catch (error) { + throw new Error(`Error adding member to organization: ${error.message}`); + } + } + +} diff --git a/services/tenant-management-service/src/providers/idp/idp-keycloak.provider.ts b/services/tenant-management-service/src/providers/idp/idp-keycloak.provider.ts index 775752d..d583006 100644 --- a/services/tenant-management-service/src/providers/idp/idp-keycloak.provider.ts +++ b/services/tenant-management-service/src/providers/idp/idp-keycloak.provider.ts @@ -1,9 +1,10 @@ -import { Provider } from "@loopback/context"; +import {Provider} from '@loopback/context'; import axios from 'axios'; import qs from 'qs'; -import { ConfigureIdpFunc, IdpDetails } from "../../types"; +import {ConfigureIdpFunc, IdpDetails} from '../../types'; interface TokenResponse { + // eslint-disable-next-line access_token: string; } @@ -13,62 +14,60 @@ interface Credentials { temporary: boolean; } -export class KeycloakIdpProvider implements Provider>{ - constructor(){} +export class KeycloakIdpProvider implements Provider> { + constructor() {} - value(): ConfigureIdpFunc { - return (payload)=>this.configure(payload); - } - async configure(payload: IdpDetails): Promise { - const { tenant } = payload; - - try { - const token=await this.authenticateAdmin(); - // 1. Create a new realm using the tenant key - await this.createRealm(tenant.key,token); - - // 2. Create a new client within the realm - const clientId = `client-${tenant.key}`; // You can customize this as needed - await this.createClient(tenant.key, clientId,token); - - // 3. Create a new admin user for the tenant - const adminUsername = `${tenant.key}-admin`; // Customize this as needed - const adminPassword = 'your-secure-password'; // This can be dynamic or set in the environment - await this.createUser(tenant.key, adminUsername, adminPassword,token); - - console.log(`Successfully configured Keycloak for tenant: ${tenant.name}`); - } catch (error) { - console.error(`Error configuring Keycloak for tenant: ${tenant.name}`, error); - throw new Error(`Failed to configure Keycloak for tenant: ${tenant.name}`); - } - } - + value(): ConfigureIdpFunc { + return payload => this.configure(payload); + } + async configure(payload: IdpDetails): Promise { + const {tenant} = payload; + try { + const token = await this.authenticateAdmin(); + // 1. Create a new realm using the tenant key + await this.createRealm(tenant.key, token); + // 2. Create a new client within the realm + const clientId = `client-${tenant.key}`; // You can customize this as needed + await this.createClient(tenant.key, clientId, token); + + // 3. Create a new admin user for the tenant + const adminUsername = `${tenant.key}-admin`; // Customize this as needed + const adminPassword = 'your-secure-password'; // This can be dynamic or set in the environment + await this.createUser(tenant.key, adminUsername, adminPassword, token); + } catch (error) { + throw new Error( + `Failed to configure Keycloak for tenant: ${tenant.name}`, + ); + } + } - async authenticateAdmin(): Promise { + async authenticateAdmin(): Promise { const response = await axios.post( - `${process.env.KEYCLOAK_HOST}/realms/master/protocol/openid-connect/token`, - qs.stringify({ - username: process.env.KEYCLOAK_ADMIN_USERNAME, - password: process.env.KEYCLOAK_ADMIN_PASSWORD, - grant_type: 'password', - client_id: 'admin-cli', - }), - { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - } + `${process.env.KEYCLOAK_HOST}/realms/master/protocol/openid-connect/token`, + qs.stringify({ + username: process.env.KEYCLOAK_ADMIN_USERNAME, + password: process.env.KEYCLOAK_ADMIN_PASSWORD, + // eslint-disable-next-line + grant_type: 'password', + // eslint-disable-next-line + client_id: 'admin-cli', + }), + { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }, ); - + return response.data.access_token; } - async createRealm(realmName: string,token:string): Promise { + async createRealm(realmName: string, token: string): Promise { // const token = await this.authenticateAdmin(); - - const response = await axios.post( + + await axios.post( `${process.env.KEYCLOAK_HOST}/admin/realms`, { realm: realmName, @@ -78,16 +77,18 @@ export class KeycloakIdpProvider implements Provider>{ headers: { Authorization: `Bearer ${token}`, }, - } + }, ); - - console.log('Realm created:', response.data); } - async createClient(realmName: string, clientId: string,token:string): Promise { + async createClient( + realmName: string, + clientId: string, + token: string, + ): Promise { // const token = await this.authenticateAdmin(); - - const response = await axios.post( + + await axios.post( `${process.env.KEYCLOAK_HOST}/admin/realms/${realmName}/clients`, { clientId: clientId, @@ -100,16 +101,19 @@ export class KeycloakIdpProvider implements Provider>{ headers: { Authorization: `Bearer ${token}`, }, - } + }, ); - - console.log('Client created:', response.data); } - async createUser(realmName: string, username: string, password: string,token:string): Promise { + async createUser( + realmName: string, + username: string, + password: string, + token: string, + ): Promise { // const token = await this.authenticateAdmin(); - - const response = await axios.post( + + await axios.post( `${process.env.KEYCLOAK_HOST}/admin/realms/${realmName}/users`, { username: username, @@ -126,13 +130,7 @@ export class KeycloakIdpProvider implements Provider>{ headers: { Authorization: `Bearer ${token}`, }, - } + }, ); - - console.log('User created:', response.data); } - - } - - diff --git a/services/tenant-management-service/src/providers/idp/index.ts b/services/tenant-management-service/src/providers/idp/index.ts index ff21cfb..0f157fc 100644 --- a/services/tenant-management-service/src/providers/idp/index.ts +++ b/services/tenant-management-service/src/providers/idp/index.ts @@ -1 +1,3 @@ -export * from './idp-keycloak.provider' \ No newline at end of file +export * from './idp-auth0.provider'; +export * from './idp-keycloak.provider'; +export * from './types'; diff --git a/services/tenant-management-service/src/providers/idp/types.ts b/services/tenant-management-service/src/providers/idp/types.ts new file mode 100644 index 0000000..d3b7c91 --- /dev/null +++ b/services/tenant-management-service/src/providers/idp/types.ts @@ -0,0 +1,47 @@ +import {PostInvitationsRequestAppMetadata} from 'auth0'; +export interface UserData { + email?: string; + // eslint-disable-next-line + phone_number?: string; + // eslint-disable-next-line + user_metadata?: {[key: string]: any}; //NOSONAR + blocked?: boolean; + // eslint-disable-next-line + email_verified?: boolean; + // eslint-disable-next-line + app_metadata?: PostInvitationsRequestAppMetadata; + // eslint-disable-next-line + given_name?: string; + // eslint-disable-next-line + family_name?: string; + name?: string; + nickname?: string; + picture?: string; + // eslint-disable-next-line + user_id?: string; + connection: string; + password?: string; + // eslint-disable-next-line + verify_email?: boolean; + username?: string; +} +export interface OrganizationData { + name: string; + // eslint-disable-next-line + display_name?: string; + // eslint-disable-next-line + logo_url?: string; + // eslint-disable-next-line + primary_color?: string; + // eslint-disable-next-line + page_background?: string; + // eslint-disable-next-line + link_color?: string; +} +export interface ConfigValue extends Omit, OrganizationData { + username: UserData['name']; +} +export type Auth0Response = { + organizationId: string; + userId: string; +}; diff --git a/services/tenant-management-service/src/repositories/index.ts b/services/tenant-management-service/src/repositories/index.ts index 3e7105b..7f7c28c 100644 --- a/services/tenant-management-service/src/repositories/index.ts +++ b/services/tenant-management-service/src/repositories/index.ts @@ -7,4 +7,3 @@ export * from './invoice.repository'; export * from './address.repository'; export * from './lead-token.repository'; export * from './tenant-config.repository'; -export * from './saas-tenant.repository'; \ No newline at end of file diff --git a/services/tenant-management-service/src/repositories/tenant-config.repository.ts b/services/tenant-management-service/src/repositories/tenant-config.repository.ts index 67941ab..752f325 100644 --- a/services/tenant-management-service/src/repositories/tenant-config.repository.ts +++ b/services/tenant-management-service/src/repositories/tenant-config.repository.ts @@ -1,27 +1,37 @@ -import { Getter, inject } from '@loopback/core'; -import { BelongsToAccessor, juggler, repository } from '@loopback/repository'; -import { DefaultUserModifyCrudRepository, IAuthUserWithPermissions } from '@sourceloop/core'; -import { SYSTEM_USER } from '../keys'; -import { Tenant, TenantConfig } from '../models'; -import { TenantManagementDbSourceName } from '../types'; -import { TenantRepository } from './tenant.repository'; +import {Getter, inject} from '@loopback/core'; +import {juggler, repository, BelongsToAccessor} from '@loopback/repository'; +import {TenantConfig, Tenant} from '../models'; +import { + DefaultUserModifyCrudRepository, + IAuthUserWithPermissions, +} from '@sourceloop/core'; +import {SYSTEM_USER} from '../keys'; +import {TenantManagementDbSourceName} from '../types'; +import {TenantRepository} from './tenant.repository'; export class TenantConfigRepository extends DefaultUserModifyCrudRepository< TenantConfig, typeof TenantConfig.prototype.id, {} > { - - public readonly tenant: BelongsToAccessor; + public readonly tenant: BelongsToAccessor< + Tenant, + typeof TenantConfig.prototype.id + >; constructor( @inject.getter(SYSTEM_USER) public readonly getCurrentUser: Getter, @inject(`datasources.${TenantManagementDbSourceName}`) - dataSource: juggler.DataSource, @repository.getter('TenantRepository') protected tenantRepositoryGetter: Getter, + dataSource: juggler.DataSource, + @repository.getter('TenantRepository') + protected tenantRepositoryGetter: Getter, ) { - super(TenantConfig, dataSource,getCurrentUser); - this.tenant = this.createBelongsToAccessorFor('tenant', tenantRepositoryGetter,); + super(TenantConfig, dataSource, getCurrentUser); + this.tenant = this.createBelongsToAccessorFor( + 'tenant', + tenantRepositoryGetter, + ); this.registerInclusionResolver('tenant', this.tenant.inclusionResolver); } } diff --git a/services/tenant-management-service/src/types/i-idp.interface.ts b/services/tenant-management-service/src/types/i-idp.interface.ts index 1b0f3fe..e381dbf 100644 --- a/services/tenant-management-service/src/types/i-idp.interface.ts +++ b/services/tenant-management-service/src/types/i-idp.interface.ts @@ -1,15 +1,14 @@ -import { Tenant } from "../models"; +import {TenantDto} from '../models/dtos/tenant-dto.model'; export enum IdPKey { - AUTH0 = 'auth0', - COGNITO = 'cognito', - KEYCLOAK = 'keycloak', - } + AUTH0 = 'auth0', + COGNITO = 'cognito', + KEYCLOAK = 'keycloak', +} -export type ConfigureIdpFunc=(payload:IdpDetails)=>Promise; - - export interface IdpDetails { - identityProvider: IdPKey; - tenant: Tenant; - } - \ No newline at end of file +export type ConfigureIdpFunc = (payload: IdpDetails) => Promise; + +export interface IdpDetails { + identityProvider: IdPKey; + tenant: TenantDto; +} diff --git a/services/tenant-management-service/src/types/index.ts b/services/tenant-management-service/src/types/index.ts index 16f8603..3ac555b 100644 --- a/services/tenant-management-service/src/types/index.ts +++ b/services/tenant-management-service/src/types/index.ts @@ -38,4 +38,4 @@ export * from './resource.type'; export * from './i-provisioning-service.interface'; export * from './i-subscription.interface'; export * from './i-event-connector.interface'; -export * from './i-idp.interface'; \ No newline at end of file +export * from './i-idp.interface'; diff --git a/services/tenant-management-service/src/webhook.component.ts b/services/tenant-management-service/src/webhook.component.ts index 39d5325..5fa722f 100644 --- a/services/tenant-management-service/src/webhook.component.ts +++ b/services/tenant-management-service/src/webhook.component.ts @@ -35,7 +35,11 @@ import { WEBHOOK_VERIFIER, } from './keys'; import {ITenantManagementServiceConfig} from './types'; -import {IdpController, TenantConfigController, TenantConfigTenantController, WebhookController} from './controllers'; +import { + TenantConfigController, + TenantConfigTenantController, + WebhookController, +} from './controllers'; import { Address, Contact, @@ -73,6 +77,9 @@ import { DEFAULT_TIMESTAMP_TOLERANCE, } from './utils'; import {ProvisioningWebhookHandler} from './services/webhook'; +import {KeycloakIdpProvider} from './providers/idp/idp-keycloak.provider'; +import {IdpController} from './controllers/idp.controller'; +import {Auth0IdpProvider} from './providers/idp'; export class WebhookTenantManagementServiceComponent implements Component { constructor( @@ -114,7 +121,7 @@ export class WebhookTenantManagementServiceComponent implements Component { TenantRepository, SaasTenantRepository, WebhookSecretRepository, - TenantConfigRepository + TenantConfigRepository, ]; this.models = [ @@ -131,14 +138,24 @@ export class WebhookTenantManagementServiceComponent implements Component { TenantOnboardDTO, VerifyLeadResponseDTO, WebhookDTO, - TenantConfig + TenantConfig, ]; - this.controllers = [WebhookController,IdpController,TenantConfigController,TenantConfigTenantController]; + this.controllers = [ + WebhookController, + IdpController, + TenantConfigController, + TenantConfigTenantController, + ]; this.bindings = [ - Binding.bind(WEBHOOK_VERIFIER).toProvider(WebhookVerifierProvider),Binding.bind(TenantManagementServiceBindings.IDP_KEYCLOAK).toProvider(KeycloakIdpProvider), - + Binding.bind(WEBHOOK_VERIFIER).toProvider(WebhookVerifierProvider), + Binding.bind(TenantManagementServiceBindings.IDP_KEYCLOAK).toProvider( + KeycloakIdpProvider, + ), + Binding.bind(TenantManagementServiceBindings.IDP_AUTH0).toProvider( + Auth0IdpProvider, + ), Binding.bind(SYSTEM_USER).toProvider(SystemUserProvider), Binding.bind(WEBHOOK_CONFIG).to({ signatureHeaderName: DEFAULT_SIGNATURE_HEADER,