diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 0000000..99e63a6 --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,21 @@ +name: e2e +on: + push: + branches: + - main + - development +jobs: + e2eTests: + name: e2e-Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: install + run: npm install + - name: e2e test + run: npm run test:e2e + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + diff --git a/.github/workflows/main.yml b/.github/workflows/sonar-unit-tests.yml similarity index 95% rename from .github/workflows/main.yml rename to .github/workflows/sonar-unit-tests.yml index f8489f0..03e3ffc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/sonar-unit-tests.yml @@ -1,4 +1,4 @@ -name: Build Sonar +name: Build Sonar Unit Tests on: push: branches: diff --git a/.gitignore b/.gitignore index f62c03f..626b825 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ lerna-debug.log* !.vscode/launch.json !.vscode/extensions.json node_modules +*.code-workspace # Sonar .scannerwork/.sonar_lock diff --git a/.scannerwork/report-task.txt b/.scannerwork/report-task.txt index dacbcf7..af55345 100644 --- a/.scannerwork/report-task.txt +++ b/.scannerwork/report-task.txt @@ -3,5 +3,5 @@ projectKey=maick05_places serverUrl=https://sonarcloud.io serverVersion=8.0.0.29264 dashboardUrl=https://sonarcloud.io/dashboard?id=maick05_places -ceTaskId=AYD9UWJG_z939kpPOwHR -ceTaskUrl=https://sonarcloud.io/api/ce/task?id=AYD9UWJG_z939kpPOwHR +ceTaskId=AYEShHKEx7rM3OF9_Y2O +ceTaskUrl=https://sonarcloud.io/api/ce/task?id=AYEShHKEx7rM3OF9_Y2O diff --git a/README.md b/README.md index 9fe8812..69c3423 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,13 @@ [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 [circleci-url]: https://circleci.com/gh/nestjs/nest -

A progressive Node.js framework for building efficient and scalable server-side applications.

-

-NPM Version -Package License -NPM Downloads -CircleCI -Coverage -Discord -Backers on Open Collective -Sponsors on Open Collective - - Support us - -

- +

This project use NestJS, a progressive Node.js framework for building efficient and scalable server-side applications.

## Description -[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. +API to get Places by Local Names. + +- Only Neighborhoods for now ## Installation @@ -58,7 +45,7 @@ $ npm run test:e2e $ npm run test:cov ``` -## Support + + +## Contact + +maicksantos05@hotmail.com + +## Example + +After run the server, make a get in browser, postman or similar + +```bash +## Url example +http://localhost:3000/neighborhoods/city/brazil/sc/orleans +``` + +[Swagger](https://app.swaggerhub.com/apis/dev-seeder/Places/1.0.0) + +At the moment, it's working only for Brazil cities. diff --git a/config/yaml/values.yaml b/config/yaml/values.yaml index b66cb4e..02fb75f 100644 --- a/config/yaml/values.yaml +++ b/config/yaml/values.yaml @@ -2,3 +2,5 @@ repository: neighborhoods: guia-mais: url: 'https://www.guiamais.com.br/bairros' +api: + port: 3000 diff --git a/package-lock.json b/package-lock.json index f41bd8e..dd62aeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,15 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@golevelup/ts-jest": "^0.3.3", "@nestjs/common": "^8.0.0", "@nestjs/config": "^2.0.1", "@nestjs/core": "^8.0.0", "@nestjs/platform-express": "^8.0.0", "chai": "^4.3.6", "cheerio": "^1.0.0-rc.10", + "class-transformer": "^0.5.1", + "class-validator": "^0.13.2", "dotenv": "^16.0.1", "fs": "^0.0.1-security", "js-yaml": "^4.1.0", @@ -23,7 +26,8 @@ "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", - "sinon": "^14.0.0" + "sinon": "^14.0.0", + "superagent": "^7.1.3" }, "devDependencies": { "@nestjs/cli": "^8.0.0", @@ -43,7 +47,7 @@ "jest": "^27.2.5", "prettier": "^2.3.2", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", + "supertest": "^6.2.3", "ts-jest": "^27.0.3", "ts-loader": "^9.2.3", "ts-node": "^10.0.0", @@ -230,21 +234,21 @@ } }, "node_modules/@babel/core": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.0.tgz", - "integrity": "sha512-Xyw74OlJwDijToNi0+6BBI5mLLR5+5R3bcSH80LXzjzEGEUlvNzujEE71BaD/ApEZHAvFI/Mlmp4M5lIkdeeWw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.2.tgz", + "integrity": "sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.18.0", - "@babel/helper-compilation-targets": "^7.17.10", + "@babel/generator": "^7.18.2", + "@babel/helper-compilation-targets": "^7.18.2", "@babel/helper-module-transforms": "^7.18.0", - "@babel/helpers": "^7.18.0", + "@babel/helpers": "^7.18.2", "@babel/parser": "^7.18.0", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.18.0", - "@babel/types": "^7.18.0", + "@babel/traverse": "^7.18.2", + "@babel/types": "^7.18.2", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -269,12 +273,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.0.tgz", - "integrity": "sha512-81YO9gGx6voPXlvYdZBliFXAZU8vZ9AZ6z+CjlmcnaeOcYSFbMTpdeDUO9xD9dh/68Vq03I8ZspfUTPfitcDHg==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz", + "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.0", + "@babel/types": "^7.18.2", "@jridgewell/gen-mapping": "^0.3.0", "jsesc": "^2.5.1" }, @@ -297,9 +301,9 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", - "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz", + "integrity": "sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==", "dev": true, "dependencies": { "@babel/compat-data": "^7.17.10", @@ -324,13 +328,10 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz", + "integrity": "sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==", "dev": true, - "dependencies": { - "@babel/types": "^7.16.7" - }, "engines": { "node": ">=6.9.0" } @@ -401,12 +402,12 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz", + "integrity": "sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==", "dev": true, "dependencies": { - "@babel/types": "^7.17.0" + "@babel/types": "^7.18.2" }, "engines": { "node": ">=6.9.0" @@ -443,14 +444,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.0.tgz", - "integrity": "sha512-AE+HMYhmlMIbho9nbvicHyxFwhrO+xhKB6AhRxzl8w46Yj0VXTZjEsAoBVC7rB2I0jzX+yWyVybnO08qkfx6kg==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.2.tgz", + "integrity": "sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==", "dev": true, "dependencies": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.18.0", - "@babel/types": "^7.18.0" + "@babel/traverse": "^7.18.2", + "@babel/types": "^7.18.2" }, "engines": { "node": ">=6.9.0" @@ -508,7 +509,7 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { @@ -542,9 +543,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-AqDccGC+m5O/iUStSJy3DGRIUFu7WbY/CppZYwrEUB4N0tZlnI8CSTsgL7v5fHVFmUbRv2sd+yy27o8Ydt4MGg==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.3.tgz", + "integrity": "sha512-rL50YcEuHbbauAFAysNsJA4/f89fGTOBRNs9P81sniKnKAr4xULe5AecolcsKbi88xu0ByWYDj/S1AJ3FSFuSQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -730,19 +731,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.0.tgz", - "integrity": "sha512-oNOO4vaoIQoGjDQ84LgtF/IAlxlyqL4TUuoQ7xLkQETFaHkY1F7yazhB4Kt3VcZGL0ZF/jhrEpnXqUb0M7V3sw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.2.tgz", + "integrity": "sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.18.0", - "@babel/helper-environment-visitor": "^7.16.7", + "@babel/generator": "^7.18.2", + "@babel/helper-environment-visitor": "^7.18.2", "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", "@babel/parser": "^7.18.0", - "@babel/types": "^7.18.0", + "@babel/types": "^7.18.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -760,9 +761,9 @@ } }, "node_modules/@babel/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.0.tgz", - "integrity": "sha512-vhAmLPAiC8j9K2GnsnLPCIH5wCrPpYIVBCWRBFDCB7Y/BXLqi/O+1RSTTM2bsmg6U/551+FCf9PNPxjABmxHTw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.2.tgz", + "integrity": "sha512-0On6B8A4/+mFUto5WERt3EEuG1NznDirvwca1O8UwXQHVY8g3R7OzYgxXdOfMwLO08UrpUD/2+3Bclyq+/C94Q==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", @@ -788,37 +789,38 @@ "node": ">=0.1.90" } }, - "node_modules/@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, "node_modules/@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { "node": ">=12" } }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz", - "integrity": "sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.2", - "globals": "^13.9.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -851,6 +853,11 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/@golevelup/ts-jest": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@golevelup/ts-jest/-/ts-jest-0.3.3.tgz", + "integrity": "sha512-gut5EhD2S7W1p+C/IsUS1o0P5SHgxsN9TqHyRTjG+rfycniLNBfvIWZPEizpsTev/lR10/XOTpE0YicxPme2+Q==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -1358,6 +1365,19 @@ "npm": ">= 6.11.0" } }, + "node_modules/@nestjs/cli/node_modules/typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@nestjs/common": { "version": "8.4.5", "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-8.4.5.tgz", @@ -1921,14 +1941,14 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.25.0.tgz", - "integrity": "sha512-icYrFnUzvm+LhW0QeJNKkezBu6tJs9p/53dpPLFH8zoM9w1tfaKzVurkPotEpAqQ8Vf8uaFyL5jHd0Vs6Z0ZQg==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.26.0.tgz", + "integrity": "sha512-oGCmo0PqnRZZndr+KwvvAUvD3kNE4AfyoGCwOZpoCncSh4MVD06JTE8XQa2u9u+NX5CsyZMBTEc2C72zx38eYA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.25.0", - "@typescript-eslint/type-utils": "5.25.0", - "@typescript-eslint/utils": "5.25.0", + "@typescript-eslint/scope-manager": "5.26.0", + "@typescript-eslint/type-utils": "5.26.0", + "@typescript-eslint/utils": "5.26.0", "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", "ignore": "^5.2.0", @@ -1954,14 +1974,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.25.0.tgz", - "integrity": "sha512-r3hwrOWYbNKP1nTcIw/aZoH+8bBnh/Lh1iDHoFpyG4DnCpvEdctrSl6LOo19fZbzypjQMHdajolxs6VpYoChgA==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.26.0.tgz", + "integrity": "sha512-n/IzU87ttzIdnAH5vQ4BBDnLPly7rC5VnjN3m0xBG82HK6rhRxnCb3w/GyWbNDghPd+NktJqB/wl6+YkzZ5T5Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.25.0", - "@typescript-eslint/types": "5.25.0", - "@typescript-eslint/typescript-estree": "5.25.0", + "@typescript-eslint/scope-manager": "5.26.0", + "@typescript-eslint/types": "5.26.0", + "@typescript-eslint/typescript-estree": "5.26.0", "debug": "^4.3.4" }, "engines": { @@ -1981,13 +2001,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.25.0.tgz", - "integrity": "sha512-p4SKTFWj+2VpreUZ5xMQsBMDdQ9XdRvODKXN4EksyBjFp2YvQdLkyHqOffakYZPuWJUDNu3jVXtHALDyTv3cww==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.26.0.tgz", + "integrity": "sha512-gVzTJUESuTwiju/7NiTb4c5oqod8xt5GhMbExKsCTp6adU3mya6AGJ4Pl9xC7x2DX9UYFsjImC0mA62BCY22Iw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.25.0", - "@typescript-eslint/visitor-keys": "5.25.0" + "@typescript-eslint/types": "5.26.0", + "@typescript-eslint/visitor-keys": "5.26.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1998,12 +2018,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.25.0.tgz", - "integrity": "sha512-B6nb3GK3Gv1Rsb2pqalebe/RyQoyG/WDy9yhj8EE0Ikds4Xa8RR28nHz+wlt4tMZk5bnAr0f3oC8TuDAd5CPrw==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.26.0.tgz", + "integrity": "sha512-7ccbUVWGLmcRDSA1+ADkDBl5fP87EJt0fnijsMFTVHXKGduYMgienC/i3QwoVhDADUAPoytgjbZbCOMj4TY55A==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.25.0", + "@typescript-eslint/utils": "5.26.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -2024,9 +2044,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.25.0.tgz", - "integrity": "sha512-7fWqfxr0KNHj75PFqlGX24gWjdV/FDBABXL5dyvBOWHpACGyveok8Uj4ipPX/1fGU63fBkzSIycEje4XsOxUFA==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.26.0.tgz", + "integrity": "sha512-8794JZFE1RN4XaExLWLI2oSXsVImNkl79PzTOOWt9h0UHROwJedNOD2IJyfL0NbddFllcktGIO2aOu10avQQyA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2037,13 +2057,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.25.0.tgz", - "integrity": "sha512-MrPODKDych/oWs/71LCnuO7NyR681HuBly2uLnX3r5i4ME7q/yBqC4hW33kmxtuauLTM0OuBOhhkFaxCCOjEEw==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.26.0.tgz", + "integrity": "sha512-EyGpw6eQDsfD6jIqmXP3rU5oHScZ51tL/cZgFbFBvWuCwrIptl+oueUZzSmLtxFuSOQ9vDcJIs+279gnJkfd1w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.25.0", - "@typescript-eslint/visitor-keys": "5.25.0", + "@typescript-eslint/types": "5.26.0", + "@typescript-eslint/visitor-keys": "5.26.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2064,15 +2084,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.25.0.tgz", - "integrity": "sha512-qNC9bhnz/n9Kba3yI6HQgQdBLuxDoMgdjzdhSInZh6NaDnFpTUlwNGxplUFWfY260Ya0TRPvkg9dd57qxrJI9g==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.26.0.tgz", + "integrity": "sha512-PJFwcTq2Pt4AMOKfe3zQOdez6InIDOjUJJD3v3LyEtxHGVVRK3Vo7Dd923t/4M9hSH2q2CLvcTdxlLPjcIk3eg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.25.0", - "@typescript-eslint/types": "5.25.0", - "@typescript-eslint/typescript-estree": "5.25.0", + "@typescript-eslint/scope-manager": "5.26.0", + "@typescript-eslint/types": "5.26.0", + "@typescript-eslint/typescript-estree": "5.26.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -2088,12 +2108,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.25.0.tgz", - "integrity": "sha512-yd26vFgMsC4h2dgX4+LR+GeicSKIfUvZREFLf3DDjZPtqgLx5AJZr6TetMNwFP9hcKreTTeztQYBTNbNoOycwA==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.26.0.tgz", + "integrity": "sha512-wei+ffqHanYDOQgg/fS6Hcar6wAWv0CUPQ3TZzOWd2BLfgP539rb49bwua8WRAs7R6kOSLn82rfEu2ro6Llt8Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.25.0", + "@typescript-eslint/types": "5.26.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -2490,8 +2510,7 @@ "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "node_modules/assertion-error": { "version": "1.1.0", @@ -2892,9 +2911,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001341", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz", - "integrity": "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==", + "version": "1.0.30001344", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz", + "integrity": "sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g==", "dev": true, "funding": [ { @@ -3050,6 +3069,20 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", + "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", + "dependencies": { + "libphonenumber-js": "^1.9.43", + "validator": "^13.7.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -3112,7 +3145,7 @@ "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, "engines": { "node": ">=0.8" @@ -3121,7 +3154,7 @@ "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "engines": { "iojs": ">= 1.0.0", @@ -3173,13 +3206,12 @@ "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concat-stream": { "version": "1.6.2", @@ -3277,13 +3309,12 @@ "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/cookiejar": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", - "dev": true + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" }, "node_modules/core-util-is": { "version": "1.0.3", @@ -3516,7 +3547,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "dev": true, "dependencies": { "asap": "^2.0.0", "wrappy": "1" @@ -3535,10 +3565,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", "engines": { "node": ">=0.3.1" } @@ -3670,9 +3699,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "node_modules/electron-to-chromium": { - "version": "1.4.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", - "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==", + "version": "1.4.140", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.140.tgz", + "integrity": "sha512-NLz5va823QfJBYOO/hLV4AfU4Crmkl/6Hl2pH3qdJcmi0ySZ3YTWHxOlDm3uJOFBEPy3pIhu8gKQo6prQTWKKA==", "dev": true }, "node_modules/emittery": { @@ -3867,12 +3896,12 @@ } }, "node_modules/eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.15.0.tgz", - "integrity": "sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.2.3", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -3890,7 +3919,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -4510,9 +4539,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", "funding": [ { "type": "individual", @@ -4594,7 +4623,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz", "integrity": "sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==", - "dev": true, "dependencies": { "dezalgo": "1.0.3", "hexoid": "1.0.0", @@ -4609,7 +4637,6 @@ "version": "6.9.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==", - "dev": true, "engines": { "node": ">=0.6" }, @@ -4865,7 +4892,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true, "engines": { "node": ">=8" } @@ -6386,6 +6412,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.6.tgz", + "integrity": "sha512-CIjT100/SmntsUjsLVs2t3ufeN4KdNXUxhD07tH153pdbaCWuAjv0jK/gPuywR3IImB/U/MQM+x9RfhMs5XZiA==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -6484,7 +6515,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -6821,9 +6851,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", - "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", + "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", "dev": true }, "node_modules/normalize-path": { @@ -6848,9 +6878,9 @@ } }, "node_modules/nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dependencies": { "boolbase": "^1.0.0" }, @@ -6881,9 +6911,9 @@ } }, "node_modules/object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7743,7 +7773,6 @@ "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -7905,14 +7934,6 @@ "@sinonjs/commons": "^1.7.0" } }, - "node_modules/sinon/node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "engines": { - "node": ">=0.3.1" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -8083,7 +8104,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.3.tgz", "integrity": "sha512-WA6et4nAvgBCS73lJvv1D0ssI5uk5Gh+TGN/kNe+B608EtcVs/yzfl+OLXTzDs7tOBDIpvgh/WUs1K2OK1zTeQ==", - "dev": true, "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.3", @@ -8105,7 +8125,6 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, "bin": { "mime": "cli.js" }, @@ -8117,7 +8136,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -8131,7 +8149,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -8578,12 +8595,12 @@ } }, "node_modules/ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.0.tgz", + "integrity": "sha512-/fNd5Qh+zTt8Vt1KbYZjRHCE9sI5i7nqfD/dzBBRDeVXZXS6kToW6R7tTU6Nd4XavFs0mAVCg29Q//ML7WsZYA==", "dev": true, "dependencies": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -8594,7 +8611,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "bin": { @@ -8629,6 +8646,15 @@ "node": ">=0.4.0" } }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", @@ -8774,9 +8800,9 @@ } }, "node_modules/typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.2.tgz", + "integrity": "sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -8868,6 +8894,14 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -9152,9 +9186,9 @@ } }, "node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz", + "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==", "dev": true, "engines": { "node": ">=8.3.0" @@ -9204,8 +9238,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "1.10.2", @@ -9398,21 +9431,21 @@ "dev": true }, "@babel/core": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.0.tgz", - "integrity": "sha512-Xyw74OlJwDijToNi0+6BBI5mLLR5+5R3bcSH80LXzjzEGEUlvNzujEE71BaD/ApEZHAvFI/Mlmp4M5lIkdeeWw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.2.tgz", + "integrity": "sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==", "dev": true, "requires": { "@ampproject/remapping": "^2.1.0", "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.18.0", - "@babel/helper-compilation-targets": "^7.17.10", + "@babel/generator": "^7.18.2", + "@babel/helper-compilation-targets": "^7.18.2", "@babel/helper-module-transforms": "^7.18.0", - "@babel/helpers": "^7.18.0", + "@babel/helpers": "^7.18.2", "@babel/parser": "^7.18.0", "@babel/template": "^7.16.7", - "@babel/traverse": "^7.18.0", - "@babel/types": "^7.18.0", + "@babel/traverse": "^7.18.2", + "@babel/types": "^7.18.2", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -9429,12 +9462,12 @@ } }, "@babel/generator": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.0.tgz", - "integrity": "sha512-81YO9gGx6voPXlvYdZBliFXAZU8vZ9AZ6z+CjlmcnaeOcYSFbMTpdeDUO9xD9dh/68Vq03I8ZspfUTPfitcDHg==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.2.tgz", + "integrity": "sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==", "dev": true, "requires": { - "@babel/types": "^7.18.0", + "@babel/types": "^7.18.2", "@jridgewell/gen-mapping": "^0.3.0", "jsesc": "^2.5.1" }, @@ -9453,9 +9486,9 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.17.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz", - "integrity": "sha512-gh3RxjWbauw/dFiU/7whjd0qN9K6nPJMqe6+Er7rOavFh0CQUSwhAE3IcTho2rywPJFxej6TUUHDkWcYI6gGqQ==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz", + "integrity": "sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==", "dev": true, "requires": { "@babel/compat-data": "^7.17.10", @@ -9473,13 +9506,10 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz", - "integrity": "sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==", - "dev": true, - "requires": { - "@babel/types": "^7.16.7" - } + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz", + "integrity": "sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==", + "dev": true }, "@babel/helper-function-name": { "version": "7.17.9", @@ -9532,12 +9562,12 @@ "dev": true }, "@babel/helper-simple-access": { - "version": "7.17.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz", - "integrity": "sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz", + "integrity": "sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==", "dev": true, "requires": { - "@babel/types": "^7.17.0" + "@babel/types": "^7.18.2" } }, "@babel/helper-split-export-declaration": { @@ -9562,14 +9592,14 @@ "dev": true }, "@babel/helpers": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.0.tgz", - "integrity": "sha512-AE+HMYhmlMIbho9nbvicHyxFwhrO+xhKB6AhRxzl8w46Yj0VXTZjEsAoBVC7rB2I0jzX+yWyVybnO08qkfx6kg==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.2.tgz", + "integrity": "sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==", "dev": true, "requires": { "@babel/template": "^7.16.7", - "@babel/traverse": "^7.18.0", - "@babel/types": "^7.18.0" + "@babel/traverse": "^7.18.2", + "@babel/types": "^7.18.2" } }, "@babel/highlight": { @@ -9615,7 +9645,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "escape-string-regexp": { @@ -9642,9 +9672,9 @@ } }, "@babel/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-AqDccGC+m5O/iUStSJy3DGRIUFu7WbY/CppZYwrEUB4N0tZlnI8CSTsgL7v5fHVFmUbRv2sd+yy27o8Ydt4MGg==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.3.tgz", + "integrity": "sha512-rL50YcEuHbbauAFAysNsJA4/f89fGTOBRNs9P81sniKnKAr4xULe5AecolcsKbi88xu0ByWYDj/S1AJ3FSFuSQ==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -9776,19 +9806,19 @@ } }, "@babel/traverse": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.0.tgz", - "integrity": "sha512-oNOO4vaoIQoGjDQ84LgtF/IAlxlyqL4TUuoQ7xLkQETFaHkY1F7yazhB4Kt3VcZGL0ZF/jhrEpnXqUb0M7V3sw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.2.tgz", + "integrity": "sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==", "dev": true, "requires": { "@babel/code-frame": "^7.16.7", - "@babel/generator": "^7.18.0", - "@babel/helper-environment-visitor": "^7.16.7", + "@babel/generator": "^7.18.2", + "@babel/helper-environment-visitor": "^7.18.2", "@babel/helper-function-name": "^7.17.9", "@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7", "@babel/parser": "^7.18.0", - "@babel/types": "^7.18.0", + "@babel/types": "^7.18.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -9802,9 +9832,9 @@ } }, "@babel/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.0.tgz", - "integrity": "sha512-vhAmLPAiC8j9K2GnsnLPCIH5wCrPpYIVBCWRBFDCB7Y/BXLqi/O+1RSTTM2bsmg6U/551+FCf9PNPxjABmxHTw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.2.tgz", + "integrity": "sha512-0On6B8A4/+mFUto5WERt3EEuG1NznDirvwca1O8UwXQHVY8g3R7OzYgxXdOfMwLO08UrpUD/2+3Bclyq+/C94Q==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.16.7", @@ -9824,31 +9854,37 @@ "dev": true, "optional": true }, - "@cspotcode/source-map-consumer": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", - "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", - "dev": true - }, "@cspotcode/source-map-support": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", - "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "requires": { - "@cspotcode/source-map-consumer": "0.8.0" + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } } }, "@eslint/eslintrc": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz", - "integrity": "sha512-uGo44hIwoLGNyduRpjdEpovcbMdd+Nv7amtmJxnKmI8xj6yd5LncmSwDa5NgX/41lIFJtkjD6YdVfgEzPfJ5UA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.3.2", - "globals": "^13.9.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -9876,6 +9912,11 @@ } } }, + "@golevelup/ts-jest": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@golevelup/ts-jest/-/ts-jest-0.3.3.tgz", + "integrity": "sha512-gut5EhD2S7W1p+C/IsUS1o0P5SHgxsN9TqHyRTjG+rfycniLNBfvIWZPEizpsTev/lR10/XOTpE0YicxPme2+Q==" + }, "@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -10276,6 +10317,14 @@ "typescript": "4.6.4", "webpack": "5.72.1", "webpack-node-externals": "3.0.0" + }, + "dependencies": { + "typescript": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", + "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "dev": true + } } }, "@nestjs/common": { @@ -10738,14 +10787,14 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.25.0.tgz", - "integrity": "sha512-icYrFnUzvm+LhW0QeJNKkezBu6tJs9p/53dpPLFH8zoM9w1tfaKzVurkPotEpAqQ8Vf8uaFyL5jHd0Vs6Z0ZQg==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.26.0.tgz", + "integrity": "sha512-oGCmo0PqnRZZndr+KwvvAUvD3kNE4AfyoGCwOZpoCncSh4MVD06JTE8XQa2u9u+NX5CsyZMBTEc2C72zx38eYA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.25.0", - "@typescript-eslint/type-utils": "5.25.0", - "@typescript-eslint/utils": "5.25.0", + "@typescript-eslint/scope-manager": "5.26.0", + "@typescript-eslint/type-utils": "5.26.0", + "@typescript-eslint/utils": "5.26.0", "debug": "^4.3.4", "functional-red-black-tree": "^1.0.1", "ignore": "^5.2.0", @@ -10755,52 +10804,52 @@ } }, "@typescript-eslint/parser": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.25.0.tgz", - "integrity": "sha512-r3hwrOWYbNKP1nTcIw/aZoH+8bBnh/Lh1iDHoFpyG4DnCpvEdctrSl6LOo19fZbzypjQMHdajolxs6VpYoChgA==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.26.0.tgz", + "integrity": "sha512-n/IzU87ttzIdnAH5vQ4BBDnLPly7rC5VnjN3m0xBG82HK6rhRxnCb3w/GyWbNDghPd+NktJqB/wl6+YkzZ5T5Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.25.0", - "@typescript-eslint/types": "5.25.0", - "@typescript-eslint/typescript-estree": "5.25.0", + "@typescript-eslint/scope-manager": "5.26.0", + "@typescript-eslint/types": "5.26.0", + "@typescript-eslint/typescript-estree": "5.26.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.25.0.tgz", - "integrity": "sha512-p4SKTFWj+2VpreUZ5xMQsBMDdQ9XdRvODKXN4EksyBjFp2YvQdLkyHqOffakYZPuWJUDNu3jVXtHALDyTv3cww==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.26.0.tgz", + "integrity": "sha512-gVzTJUESuTwiju/7NiTb4c5oqod8xt5GhMbExKsCTp6adU3mya6AGJ4Pl9xC7x2DX9UYFsjImC0mA62BCY22Iw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.25.0", - "@typescript-eslint/visitor-keys": "5.25.0" + "@typescript-eslint/types": "5.26.0", + "@typescript-eslint/visitor-keys": "5.26.0" } }, "@typescript-eslint/type-utils": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.25.0.tgz", - "integrity": "sha512-B6nb3GK3Gv1Rsb2pqalebe/RyQoyG/WDy9yhj8EE0Ikds4Xa8RR28nHz+wlt4tMZk5bnAr0f3oC8TuDAd5CPrw==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.26.0.tgz", + "integrity": "sha512-7ccbUVWGLmcRDSA1+ADkDBl5fP87EJt0fnijsMFTVHXKGduYMgienC/i3QwoVhDADUAPoytgjbZbCOMj4TY55A==", "dev": true, "requires": { - "@typescript-eslint/utils": "5.25.0", + "@typescript-eslint/utils": "5.26.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.25.0.tgz", - "integrity": "sha512-7fWqfxr0KNHj75PFqlGX24gWjdV/FDBABXL5dyvBOWHpACGyveok8Uj4ipPX/1fGU63fBkzSIycEje4XsOxUFA==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.26.0.tgz", + "integrity": "sha512-8794JZFE1RN4XaExLWLI2oSXsVImNkl79PzTOOWt9h0UHROwJedNOD2IJyfL0NbddFllcktGIO2aOu10avQQyA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.25.0.tgz", - "integrity": "sha512-MrPODKDych/oWs/71LCnuO7NyR681HuBly2uLnX3r5i4ME7q/yBqC4hW33kmxtuauLTM0OuBOhhkFaxCCOjEEw==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.26.0.tgz", + "integrity": "sha512-EyGpw6eQDsfD6jIqmXP3rU5oHScZ51tL/cZgFbFBvWuCwrIptl+oueUZzSmLtxFuSOQ9vDcJIs+279gnJkfd1w==", "dev": true, "requires": { - "@typescript-eslint/types": "5.25.0", - "@typescript-eslint/visitor-keys": "5.25.0", + "@typescript-eslint/types": "5.26.0", + "@typescript-eslint/visitor-keys": "5.26.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10809,26 +10858,26 @@ } }, "@typescript-eslint/utils": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.25.0.tgz", - "integrity": "sha512-qNC9bhnz/n9Kba3yI6HQgQdBLuxDoMgdjzdhSInZh6NaDnFpTUlwNGxplUFWfY260Ya0TRPvkg9dd57qxrJI9g==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.26.0.tgz", + "integrity": "sha512-PJFwcTq2Pt4AMOKfe3zQOdez6InIDOjUJJD3v3LyEtxHGVVRK3Vo7Dd923t/4M9hSH2q2CLvcTdxlLPjcIk3eg==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", - "@typescript-eslint/scope-manager": "5.25.0", - "@typescript-eslint/types": "5.25.0", - "@typescript-eslint/typescript-estree": "5.25.0", + "@typescript-eslint/scope-manager": "5.26.0", + "@typescript-eslint/types": "5.26.0", + "@typescript-eslint/typescript-estree": "5.26.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/visitor-keys": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.25.0.tgz", - "integrity": "sha512-yd26vFgMsC4h2dgX4+LR+GeicSKIfUvZREFLf3DDjZPtqgLx5AJZr6TetMNwFP9hcKreTTeztQYBTNbNoOycwA==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.26.0.tgz", + "integrity": "sha512-wei+ffqHanYDOQgg/fS6Hcar6wAWv0CUPQ3TZzOWd2BLfgP539rb49bwua8WRAs7R6kOSLn82rfEu2ro6Llt8Q==", "dev": true, "requires": { - "@typescript-eslint/types": "5.25.0", + "@typescript-eslint/types": "5.26.0", "eslint-visitor-keys": "^3.3.0" } }, @@ -11155,8 +11204,7 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "assertion-error": { "version": "1.1.0", @@ -11455,9 +11503,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001341", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001341.tgz", - "integrity": "sha512-2SodVrFFtvGENGCv0ChVJIDQ0KPaS1cg7/qtfMaICgeMolDdo/Z2OD32F0Aq9yl6F4YFwGPBS5AaPqNYiW4PoA==", + "version": "1.0.30001344", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz", + "integrity": "sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g==", "dev": true }, "chai": { @@ -11568,6 +11616,20 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, + "class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "class-validator": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.13.2.tgz", + "integrity": "sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==", + "requires": { + "libphonenumber-js": "^1.9.43", + "validator": "^13.7.0" + } + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -11613,13 +11675,13 @@ "clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true }, "collect-v8-coverage": { @@ -11658,13 +11720,12 @@ "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "concat-stream": { "version": "1.6.2", @@ -11754,13 +11815,12 @@ "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "cookiejar": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", - "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==", - "dev": true + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" }, "core-util-is": { "version": "1.0.3", @@ -11944,7 +12004,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz", "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", - "dev": true, "requires": { "asap": "^2.0.0", "wrappy": "1" @@ -11960,10 +12019,9 @@ } }, "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" }, "diff-sequences": { "version": "27.5.1", @@ -12055,9 +12113,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.4.137", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz", - "integrity": "sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA==", + "version": "1.4.140", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.140.tgz", + "integrity": "sha512-NLz5va823QfJBYOO/hLV4AfU4Crmkl/6Hl2pH3qdJcmi0ySZ3YTWHxOlDm3uJOFBEPy3pIhu8gKQo6prQTWKKA==", "dev": true }, "emittery": { @@ -12200,12 +12258,12 @@ } }, "eslint": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.15.0.tgz", - "integrity": "sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.16.0.tgz", + "integrity": "sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.2.3", + "@eslint/eslintrc": "^1.3.0", "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -12223,7 +12281,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", - "globals": "^13.6.0", + "globals": "^13.15.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -12703,9 +12761,9 @@ "dev": true }, "follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==" + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" }, "fork-ts-checker-webpack-plugin": { "version": "7.2.11", @@ -12752,7 +12810,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.0.1.tgz", "integrity": "sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==", - "dev": true, "requires": { "dezalgo": "1.0.3", "hexoid": "1.0.0", @@ -12763,8 +12820,7 @@ "qs": { "version": "6.9.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.3.tgz", - "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==", - "dev": true + "integrity": "sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==" } } }, @@ -12945,8 +13001,7 @@ "hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==" }, "html-encoding-sniffer": { "version": "2.0.1", @@ -14098,6 +14153,11 @@ "type-check": "~0.4.0" } }, + "libphonenumber-js": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.6.tgz", + "integrity": "sha512-CIjT100/SmntsUjsLVs2t3ufeN4KdNXUxhD07tH153pdbaCWuAjv0jK/gPuywR3IImB/U/MQM+x9RfhMs5XZiA==" + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -14180,7 +14240,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -14444,9 +14503,9 @@ "dev": true }, "node-releases": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.4.tgz", - "integrity": "sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz", + "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", "dev": true }, "normalize-path": { @@ -14465,9 +14524,9 @@ } }, "nth-check": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", - "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "requires": { "boolbase": "^1.0.0" } @@ -14489,9 +14548,9 @@ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" }, "object-inspect": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", - "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, "on-finished": { "version": "2.4.1", @@ -15089,7 +15148,6 @@ "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -15223,11 +15281,6 @@ "requires": { "@sinonjs/commons": "^1.7.0" } - }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" } } }, @@ -15363,7 +15416,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.3.tgz", "integrity": "sha512-WA6et4nAvgBCS73lJvv1D0ssI5uk5Gh+TGN/kNe+B608EtcVs/yzfl+OLXTzDs7tOBDIpvgh/WUs1K2OK1zTeQ==", - "dev": true, "requires": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.3", @@ -15381,14 +15433,12 @@ "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15399,7 +15449,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -15725,12 +15774,12 @@ } }, "ts-node": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", - "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.8.0.tgz", + "integrity": "sha512-/fNd5Qh+zTt8Vt1KbYZjRHCE9sI5i7nqfD/dzBBRDeVXZXS6kToW6R7tTU6Nd4XavFs0mAVCg29Q//ML7WsZYA==", "dev": true, "requires": { - "@cspotcode/source-map-support": "0.7.0", + "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", "@tsconfig/node14": "^1.0.0", @@ -15741,7 +15790,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "dependencies": { @@ -15750,6 +15799,12 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true } } }, @@ -15871,9 +15926,9 @@ } }, "typescript": { - "version": "4.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz", - "integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.2.tgz", + "integrity": "sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==", "dev": true }, "unbzip2-stream": { @@ -15943,6 +15998,11 @@ "source-map": "^0.7.3" } }, + "validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -16157,9 +16217,9 @@ } }, "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.8.tgz", + "integrity": "sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==", "dev": true, "requires": {} }, @@ -16189,8 +16249,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index 93d6690..c75fb57 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,15 @@ "test:e2e": "jest --config ./test/e2e/jest-e2e.json" }, "dependencies": { + "@golevelup/ts-jest": "^0.3.3", "@nestjs/common": "^8.0.0", "@nestjs/config": "^2.0.1", "@nestjs/core": "^8.0.0", "@nestjs/platform-express": "^8.0.0", "chai": "^4.3.6", "cheerio": "^1.0.0-rc.10", + "class-transformer": "^0.5.1", + "class-validator": "^0.13.2", "dotenv": "^16.0.1", "fs": "^0.0.1-security", "js-yaml": "^4.1.0", @@ -35,7 +38,8 @@ "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0", - "sinon": "^14.0.0" + "sinon": "^14.0.0", + "superagent": "^7.1.3" }, "devDependencies": { "@nestjs/cli": "^8.0.0", @@ -55,7 +59,7 @@ "jest": "^27.2.5", "prettier": "^2.3.2", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", + "supertest": "^6.2.3", "ts-jest": "^27.0.3", "ts-loader": "^9.2.3", "ts-node": "^10.0.0", @@ -78,7 +82,8 @@ "coverageDirectory": "coverage", "coveragePathIgnorePatterns": [ "/node_modules/", - "/src/main.ts" + "/src/main.ts", + "/src/app.module.ts" ], "testEnvironment": "node" } diff --git a/sonar-project.properties b/sonar-project.properties index 566714d..6b0da3c 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -23,7 +23,7 @@ sonar.test.inclusions=test/**/*.spec.ts sonar.tests=./test -sonar.exclusions= .eslintrc.js, src/main.ts, src/test/*, test/app.e2e-spec.ts +sonar.exclusions= .eslintrc.js, src/main.ts, src/test/*, test/app.e2e-spec.ts, src/app.module.ts sonar.typescript.tsconfigPath=tsconfig.json diff --git a/src/adapter/controller/neighborhoods.controller.ts b/src/adapter/controller/neighborhoods.controller.ts deleted file mode 100644 index b1d30cf..0000000 --- a/src/adapter/controller/neighborhoods.controller.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Controller, Get, Param } from '@nestjs/common'; -import { NeighborhoodsService } from '../service/neighborhoods.service'; - -@Controller('neighborhoods') -export class NeighborhoodsController { - constructor(private readonly neighborhoodsService: NeighborhoodsService) {} - - @Get('/city/:country/:state/:city') - getNeighborhoodsByCity( - @Param('country') country, - @Param('state') state, - @Param('city') city - ): Promise { - return this.neighborhoodsService.getNeighborhoodsByCity( - country, - state, - city - ); - } -} diff --git a/src/app.module.ts b/src/app.module.ts index 849f3e2..19c8308 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,10 +1,14 @@ -import { Module } from '@nestjs/common'; +import { ClassSerializerInterceptor, Module } from '@nestjs/common'; +import { APP_INTERCEPTOR } from '@nestjs/core'; import { PuppeteerModule } from 'nest-puppeteer'; -import { ExtensionsModule } from './adapter/helper/extensions/exensions.module'; -import { NeighborhoodsModule } from './adapter/neighborhoods.module'; +import { FiltersModule } from './core/error-handling/filters.module'; +import { TransformResponseInterceptor } from './core/http/transform-response.interceptor'; +import { ExtensionsModule } from './microservice/adapter/helper/extensions/exensions.module'; +import { NeighborhoodsModule } from './microservice/adapter/neighborhoods.module'; @Module({ imports: [ + FiltersModule, ExtensionsModule, PuppeteerModule.forRoot({ isGlobal: true @@ -12,6 +16,15 @@ import { NeighborhoodsModule } from './adapter/neighborhoods.module'; NeighborhoodsModule ], controllers: [], - providers: [] + providers: [ + { + provide: APP_INTERCEPTOR, + useClass: ClassSerializerInterceptor + }, + { + provide: APP_INTERCEPTOR, + useClass: TransformResponseInterceptor + } + ] }) export class AppModule {} diff --git a/src/core/error-handling/exception/custom-error.exception.ts b/src/core/error-handling/exception/custom-error.exception.ts new file mode 100644 index 0000000..9951cc1 --- /dev/null +++ b/src/core/error-handling/exception/custom-error.exception.ts @@ -0,0 +1,15 @@ +import { HttpException } from '@nestjs/common'; + +export class CustomErrorException extends HttpException { + constructor( + public message: string, + status: number, + public errCode: number = -1 + ) { + super(message, status); + } + + get type(): string { + return this.constructor.name; + } +} diff --git a/src/core/error-handling/exception/empty-data.exception.ts b/src/core/error-handling/exception/empty-data.exception.ts new file mode 100644 index 0000000..0a6265f --- /dev/null +++ b/src/core/error-handling/exception/empty-data.exception.ts @@ -0,0 +1,12 @@ +import { HttpStatus } from '@nestjs/common'; +import { CustomErrorException } from './custom-error.exception'; + +export class EmptyDataException extends CustomErrorException { + constructor(element = '') { + super( + `The ${element} data cannot be empty`, + HttpStatus.INTERNAL_SERVER_ERROR, + 1 + ); + } +} diff --git a/src/core/error-handling/exception/empty-prop.exception.ts b/src/core/error-handling/exception/empty-prop.exception.ts new file mode 100644 index 0000000..163d20d --- /dev/null +++ b/src/core/error-handling/exception/empty-prop.exception.ts @@ -0,0 +1,12 @@ +import { HttpStatus } from '@nestjs/common'; +import { CustomErrorException } from './custom-error.exception'; + +export class EmptyPropException extends CustomErrorException { + constructor(element: string) { + super( + `The property '${element.capitalize()}' cannot be empty`, + HttpStatus.NOT_ACCEPTABLE, + 2 + ); + } +} diff --git a/src/core/error-handling/exception/not-found.exception.ts b/src/core/error-handling/exception/not-found.exception.ts new file mode 100644 index 0000000..72ad3f6 --- /dev/null +++ b/src/core/error-handling/exception/not-found.exception.ts @@ -0,0 +1,12 @@ +import { HttpStatus } from '@nestjs/common'; +import { CustomErrorException } from './custom-error.exception'; + +export class NotFoundException extends CustomErrorException { + constructor(element: string) { + super( + `${element.capitalize()} not found`, + HttpStatus.NOT_FOUND, + HttpStatus.NOT_FOUND + ); + } +} diff --git a/src/core/error-handling/filter/abstract-exception.filter.ts b/src/core/error-handling/filter/abstract-exception.filter.ts new file mode 100644 index 0000000..4d25074 --- /dev/null +++ b/src/core/error-handling/filter/abstract-exception.filter.ts @@ -0,0 +1,33 @@ +import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common'; +import { AbstractHttpAdapter, HttpAdapterHost } from '@nestjs/core'; +import { CustomExceptionReponse } from '../interface/custom-exception-response.interface'; + +@Catch() +export abstract class AbstractExceptionFilter + implements ExceptionFilter +{ + protected httpAdapter: AbstractHttpAdapter; + constructor(adapterHost: HttpAdapterHost) { + this.httpAdapter = adapterHost.httpAdapter; + } + + getResponse( + host: ArgumentsHost, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _exception: ExceptionType + ): Record { + return host.switchToHttp().getResponse(); + } + + catch(exception: ExceptionType, host: ArgumentsHost) { + const response = this.getResponse(host, exception); + + const customResponse = this.makeCustomResponse(exception); + + console.error(exception); + + this.httpAdapter.reply(response, customResponse, customResponse.status); + } + + abstract makeCustomResponse(exception: ExceptionType): CustomExceptionReponse; +} diff --git a/src/core/error-handling/filter/custom-error-exception.filter.ts b/src/core/error-handling/filter/custom-error-exception.filter.ts new file mode 100644 index 0000000..d9fd4b6 --- /dev/null +++ b/src/core/error-handling/filter/custom-error-exception.filter.ts @@ -0,0 +1,16 @@ +import { Catch } from '@nestjs/common'; +import { CustomErrorException } from '../exception/custom-error.exception'; +import { AbstractExceptionFilter } from './abstract-exception.filter'; +import { CustomExceptionReponse } from '../interface/custom-exception-response.interface'; + +@Catch(CustomErrorException) +export class CustomErrorExceptionFilter extends AbstractExceptionFilter { + makeCustomResponse(exception: CustomErrorException): CustomExceptionReponse { + return { + status: exception.getStatus(), + message: exception.message, + type: exception.type, + errorCode: exception.errCode + }; + } +} diff --git a/src/core/error-handling/filter/error-exception.filter.ts b/src/core/error-handling/filter/error-exception.filter.ts new file mode 100644 index 0000000..c2f6d6f --- /dev/null +++ b/src/core/error-handling/filter/error-exception.filter.ts @@ -0,0 +1,34 @@ +import { Catch, HttpException, HttpStatus } from '@nestjs/common'; +import { AbstractExceptionFilter } from './abstract-exception.filter'; +import { CustomExceptionReponse } from '../interface/custom-exception-response.interface'; +import { CustomErrorException } from '../exception/custom-error.exception'; + +@Catch() +export class ErrorExceptionFilter extends AbstractExceptionFilter { + makeCustomResponse(exception: any): CustomExceptionReponse { + return { + status: this.getStatus(exception), + message: exception.message, + type: exception.name, + errorCode: this.getErrCode(exception) + }; + } + + getStatus(exception: any): number { + let status = HttpStatus.INTERNAL_SERVER_ERROR; + + if (exception instanceof HttpException) status = exception.getStatus(); + + return status; + } + + getErrCode(exception: any): number { + let errCode = -1; + + if (exception instanceof HttpException) errCode = exception.getStatus(); + + if (exception instanceof CustomErrorException) errCode = exception.errCode; + + return errCode; + } +} diff --git a/src/core/error-handling/filter/http-exception.filter.ts b/src/core/error-handling/filter/http-exception.filter.ts new file mode 100644 index 0000000..56b7e89 --- /dev/null +++ b/src/core/error-handling/filter/http-exception.filter.ts @@ -0,0 +1,19 @@ +import { Catch, ArgumentsHost, HttpException } from '@nestjs/common'; +import { AbstractExceptionFilter } from './abstract-exception.filter'; +import { CustomExceptionReponse } from '../interface/custom-exception-response.interface'; + +@Catch(HttpException) +export class HttpExceptionFilter extends AbstractExceptionFilter { + getResponse(_host: ArgumentsHost, exception: HttpException): any { + return exception.getResponse(); + } + + makeCustomResponse(exception: HttpException): CustomExceptionReponse { + return { + status: exception.getStatus(), + message: exception.message, + type: exception.name, + errorCode: exception.getStatus() + }; + } +} diff --git a/src/core/error-handling/filters.module.ts b/src/core/error-handling/filters.module.ts new file mode 100644 index 0000000..086560e --- /dev/null +++ b/src/core/error-handling/filters.module.ts @@ -0,0 +1,25 @@ +import { Module } from '@nestjs/common'; +import { APP_FILTER } from '@nestjs/core'; +import { CustomErrorExceptionFilter } from './filter/custom-error-exception.filter'; +import { ErrorExceptionFilter } from './filter/error-exception.filter'; +import { HttpExceptionFilter } from './filter/http-exception.filter'; + +@Module({ + imports: [], + controllers: [], + providers: [ + { + provide: APP_FILTER, + useClass: HttpExceptionFilter + }, + { + provide: APP_FILTER, + useClass: CustomErrorExceptionFilter + }, + { + provide: APP_FILTER, + useClass: ErrorExceptionFilter + } + ] +}) +export class FiltersModule {} diff --git a/src/core/error-handling/interface/custom-exception-response.interface.ts b/src/core/error-handling/interface/custom-exception-response.interface.ts new file mode 100644 index 0000000..b2d9e71 --- /dev/null +++ b/src/core/error-handling/interface/custom-exception-response.interface.ts @@ -0,0 +1,6 @@ +export interface CustomExceptionReponse { + status: number; + message: string; + type: string; + errorCode: number; +} diff --git a/src/core/http/nest-response.builder.ts b/src/core/http/nest-response.builder.ts new file mode 100644 index 0000000..2cefe62 --- /dev/null +++ b/src/core/http/nest-response.builder.ts @@ -0,0 +1,33 @@ +import { CustomResponse } from '../interface/custom-response.interface'; +import { NestResponse } from './nest-response'; + +export class NestResponseBuilder { + protected response: NestResponse = { + status: 200, + headers: {}, + body: { + success: true, + response: '' + } + }; + + setStatus(status: number) { + this.response.status = status; + return this; + } + + setHeader(headers: any) { + if (Object.keys(headers).length === 0) return this; + this.response.headers = headers; + return this; + } + + setBody(body: CustomResponse) { + this.response.body = body; + return this; + } + + build() { + return new NestResponse(this.response); + } +} diff --git a/src/core/http/nest-response.ts b/src/core/http/nest-response.ts new file mode 100644 index 0000000..9990009 --- /dev/null +++ b/src/core/http/nest-response.ts @@ -0,0 +1,11 @@ +import { CustomResponse } from '../interface/custom-response.interface'; + +export class NestResponse { + status: number; + headers: object; + body: CustomResponse; + + constructor(response: NestResponse) { + Object.assign(this, response); + } +} diff --git a/src/core/http/transform-response.interceptor.ts b/src/core/http/transform-response.interceptor.ts new file mode 100644 index 0000000..8b089dc --- /dev/null +++ b/src/core/http/transform-response.interceptor.ts @@ -0,0 +1,52 @@ +import { + NestInterceptor, + Injectable, + ExecutionContext, + CallHandler +} from '@nestjs/common'; + +import { AbstractHttpAdapter, HttpAdapterHost } from '@nestjs/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { NestResponse } from './nest-response'; + +@Injectable() +export class TransformResponseInterceptor implements NestInterceptor { + private readonly httpAdapter: AbstractHttpAdapter; + + constructor(adapterHost: HttpAdapterHost) { + this.httpAdapter = adapterHost.httpAdapter; + } + + intercept( + context: ExecutionContext, + next: CallHandler + ): Observable | Promise> { + return next.handle().pipe( + /* istanbul ignore next */ + map((responseController: NestResponse) => { + return this.interceptResponse(responseController, context); + }) + ); + } + + interceptResponse( + responseController: NestResponse, + context: ExecutionContext + ) { + const ctx = context.switchToHttp(); + const response = ctx.getResponse(); + const { headers, status, body } = responseController; + + const headersName = Object.getOwnPropertyNames(headers); + + headersName.forEach((header) => { + const headerValue = headers[header]; + this.httpAdapter.setHeader(response, header, headerValue); + }); + + this.httpAdapter.status(response, status); + + return body; + } +} diff --git a/src/core/interface/custom-response.interface.ts b/src/core/interface/custom-response.interface.ts new file mode 100644 index 0000000..f6118de --- /dev/null +++ b/src/core/interface/custom-response.interface.ts @@ -0,0 +1,4 @@ +export interface CustomResponse { + success: boolean; + response: object | string | number; +} diff --git a/src/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts b/src/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts deleted file mode 100644 index f7b80cb..0000000 --- a/src/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import { CheerioAPI } from 'cheerio'; -import { NeighborhoodsByCity } from 'src/domain/model/neighborhoods-by-city.model'; -import { SearchNeighborhoods } from 'src/domain/model/search/search-neighborhoods.model'; -import { PuppeteerRepository } from '../puppeteer.repository'; -import { IPuppeteerNeighborhoodRepository } from '../../../interfaces/puppeteer/repository/puppeteer-neighborhood-repository.interface'; - -export abstract class PuppeteerNeighborhoodRepository - extends PuppeteerRepository - implements IPuppeteerNeighborhoodRepository -{ - async getNeighborhoodsByCity( - searchParams: SearchNeighborhoods - ): Promise { - const $ = await this.callEndpoint(searchParams); - return this.buildElementFromDocument(searchParams, $); - } - - /* istanbul ignore next */ - buildElementFromDocument( - _searchParams: SearchNeighborhoods, - _$: CheerioAPI - ): NeighborhoodsByCity[] { - throw new Error('Method not implemented.'); - } - - /* istanbul ignore next */ - async callEndpoint(_searchParams: SearchNeighborhoods): Promise { - throw new Error('Method not implemented.'); - } -} diff --git a/src/main.ts b/src/main.ts index 13cad38..7e3f12a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,19 @@ +import { ValidationPipe } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; import { NestFactory } from '@nestjs/core'; +import { useContainer } from 'class-validator'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); - await app.listen(3000); + app.useGlobalPipes( + new ValidationPipe({ + transform: true + }) + ); + useContainer(app.select(AppModule), { fallbackOnErrors: true }); + const configService = app.get(ConfigService); + return await app.listen(configService.get('api.port')); } -bootstrap(); + +module.exports = bootstrap(); diff --git a/src/microservice/adapter/controller/neighborhoods.controller.ts b/src/microservice/adapter/controller/neighborhoods.controller.ts new file mode 100644 index 0000000..7f52f7b --- /dev/null +++ b/src/microservice/adapter/controller/neighborhoods.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get, HttpStatus, Param } from '@nestjs/common'; +import { NestResponse } from '../../../core/http/nest-response'; +import { AbstractController } from '../../domain/controller/abstract-controller'; +import { NeighborhoodsService } from '../service/neighborhoods.service'; + +@Controller('neighborhoods') +export class NeighborhoodsController extends AbstractController { + constructor(private readonly neighborhoodsService: NeighborhoodsService) { + super(); + } + + @Get('/city/:country/:state/:city') + getNeighborhoodsByCity( + @Param('country') country, + @Param('state') state, + @Param('city') city + ): NestResponse { + return this.buildResponse( + HttpStatus.OK, + this.neighborhoodsService.getNeighborhoodsByCity(country, state, city) + ); + } +} diff --git a/src/adapter/helper/extensions/exensions.module.ts b/src/microservice/adapter/helper/extensions/exensions.module.ts similarity index 81% rename from src/adapter/helper/extensions/exensions.module.ts rename to src/microservice/adapter/helper/extensions/exensions.module.ts index 0489ae4..acfc2c1 100644 --- a/src/adapter/helper/extensions/exensions.module.ts +++ b/src/microservice/adapter/helper/extensions/exensions.module.ts @@ -1,4 +1,5 @@ import './string.extension'; +import './object.extension'; import { Module } from '@nestjs/common'; @Module({ diff --git a/src/microservice/adapter/helper/extensions/object.extension.ts b/src/microservice/adapter/helper/extensions/object.extension.ts new file mode 100644 index 0000000..3662288 --- /dev/null +++ b/src/microservice/adapter/helper/extensions/object.extension.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-this-alias */ +import { EmptyPropException } from '../../../../core/error-handling/exception/empty-prop.exception'; + +declare global { + interface Object { + validateIsAnyEmptyKey(): void; + getMethods(): string[]; + } +} + +Object.prototype.validateIsAnyEmptyKey = function () { + const context = this; + Object.keys(this).forEach(function (key) { + if (context[key].length === 0) throw new EmptyPropException(key); + }); +}; + +Object.prototype.getMethods = (): any[] => { + const properties = new Set(); + let currentObj = this; + const obj = this; + do { + Object.getOwnPropertyNames(currentObj).forEach((item) => + properties.add(item) + ); + } while ((currentObj = Object.getPrototypeOf(currentObj))); + return [...properties.keys()].filter( + (item: string) => + typeof obj !== 'undefined' && typeof obj[item] === 'function' + ); +}; + +export {}; diff --git a/src/adapter/helper/extensions/string.extension.ts b/src/microservice/adapter/helper/extensions/string.extension.ts similarity index 100% rename from src/adapter/helper/extensions/string.extension.ts rename to src/microservice/adapter/helper/extensions/string.extension.ts diff --git a/src/adapter/neighborhoods.module.ts b/src/microservice/adapter/neighborhoods.module.ts similarity index 92% rename from src/adapter/neighborhoods.module.ts rename to src/microservice/adapter/neighborhoods.module.ts index 8125e9d..9875e43 100644 --- a/src/adapter/neighborhoods.module.ts +++ b/src/microservice/adapter/neighborhoods.module.ts @@ -4,7 +4,7 @@ import { PuppeteerModule } from 'nest-puppeteer'; import { NeighborhoodsController } from './controller/neighborhoods.controller'; import { GuiaMaisRepository } from './repository/neighborhoods/guia-mais.repository'; import { NeighborhoodsService } from './service/neighborhoods.service'; -import configuration from '../config/configuration'; +import configuration from '../../config/configuration'; @Module({ imports: [ diff --git a/src/adapter/repository/neighborhoods/guia-mais.repository.ts b/src/microservice/adapter/repository/neighborhoods/guia-mais.repository.ts similarity index 83% rename from src/adapter/repository/neighborhoods/guia-mais.repository.ts rename to src/microservice/adapter/repository/neighborhoods/guia-mais.repository.ts index fdcf050..6e5fb58 100644 --- a/src/adapter/repository/neighborhoods/guia-mais.repository.ts +++ b/src/microservice/adapter/repository/neighborhoods/guia-mais.repository.ts @@ -2,11 +2,11 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { CheerioAPI } from 'cheerio'; import { InjectPage } from 'nest-puppeteer'; -import { IPuppeteerNeighborhoodRepository } from '../../../domain/interfaces/puppeteer/repository/puppeteer-neighborhood-repository.interface'; -import { PuppeteerNeighborhoodRepository } from '../../../domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository'; import { NeighborhoodsByCity } from '../../../domain/model/neighborhoods-by-city.model'; import { SearchNeighborhoods } from '../../../domain/model/search/search-neighborhoods.model'; -import { Page } from '../../../domain/interfaces/puppeteer/page.interface'; +import { IPuppeteerNeighborhoodRepository } from '../../../domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface'; +import { PuppeteerNeighborhoodRepository } from '../../../domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository'; +import { Page } from '../../../domain/interface/puppeteer/page.interface'; @Injectable() export class GuiaMaisRepository @@ -23,7 +23,10 @@ export class GuiaMaisRepository ); } - buildElementFromDocument(searchParams, $: CheerioAPI): NeighborhoodsByCity[] { + buildElementsFromDocument( + searchParams, + $: CheerioAPI + ): NeighborhoodsByCity[] { const arrNeighborhoods = []; $('.cities.centerContent') .find('a') diff --git a/src/adapter/service/neighborhoods.service.ts b/src/microservice/adapter/service/neighborhoods.service.ts similarity index 89% rename from src/adapter/service/neighborhoods.service.ts rename to src/microservice/adapter/service/neighborhoods.service.ts index c0de949..04c365c 100644 --- a/src/adapter/service/neighborhoods.service.ts +++ b/src/microservice/adapter/service/neighborhoods.service.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { INeighborhoodsService } from 'src/domain/service/neighborhoods-service.interface'; import { NeighborhoodsByCity } from '../../domain/model/neighborhoods-by-city.model'; import { SearchNeighborhoods } from '../../domain/model/search/search-neighborhoods.model'; +import { INeighborhoodsService } from '../../domain/service/neighborhoods-service.interface'; + import { GuiaMaisRepository } from '../repository/neighborhoods/guia-mais.repository'; @Injectable() diff --git a/src/microservice/domain/controller/abstract-controller.ts b/src/microservice/domain/controller/abstract-controller.ts new file mode 100644 index 0000000..d942f58 --- /dev/null +++ b/src/microservice/domain/controller/abstract-controller.ts @@ -0,0 +1,11 @@ +import { NestResponseBuilder } from '../../../core/http/nest-response.builder'; + +export abstract class AbstractController { + buildResponse(status, body, header = {}) { + return new NestResponseBuilder() + .setStatus(status) + .setHeader(header) + .setBody(body) + .build(); + } +} diff --git a/src/domain/interfaces/puppeteer/page.interface.ts b/src/microservice/domain/interface/puppeteer/page.interface.ts similarity index 100% rename from src/domain/interfaces/puppeteer/page.interface.ts rename to src/microservice/domain/interface/puppeteer/page.interface.ts diff --git a/src/domain/interfaces/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts b/src/microservice/domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts similarity index 79% rename from src/domain/interfaces/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts rename to src/microservice/domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts index f202f81..063ff0c 100644 --- a/src/domain/interfaces/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts +++ b/src/microservice/domain/interface/puppeteer/repository/puppeteer-neighborhood-repository.interface.ts @@ -1,4 +1,4 @@ -import { SearchNeighborhoods } from 'src/domain/model/search/search-neighborhoods.model'; +import { SearchNeighborhoods } from '../../../model/search/search-neighborhoods.model'; import { NeighborhoodsByCity } from '../../../model/neighborhoods-by-city.model'; import { IPuppeteerRepository } from './puppeteer-repository.interface'; diff --git a/src/domain/interfaces/puppeteer/repository/puppeteer-repository.interface.ts b/src/microservice/domain/interface/puppeteer/repository/puppeteer-repository.interface.ts similarity index 71% rename from src/domain/interfaces/puppeteer/repository/puppeteer-repository.interface.ts rename to src/microservice/domain/interface/puppeteer/repository/puppeteer-repository.interface.ts index f93543d..49a40bd 100644 --- a/src/domain/interfaces/puppeteer/repository/puppeteer-repository.interface.ts +++ b/src/microservice/domain/interface/puppeteer/repository/puppeteer-repository.interface.ts @@ -9,8 +9,12 @@ export interface IPuppeteerRepository { goToUrl(url: string): Promise; - buildElementFromDocument( + buildElementsFromDocument( searchParams: SearchElement, $: CheerioAPI ): ElementPlace[]; + + validateInput(searchParams: SearchElement): void; + + validateOutput(output: ElementPlace[]): void; } diff --git a/src/domain/model/neighborhoods-by-city.model.ts b/src/microservice/domain/model/neighborhoods-by-city.model.ts similarity index 100% rename from src/domain/model/neighborhoods-by-city.model.ts rename to src/microservice/domain/model/neighborhoods-by-city.model.ts diff --git a/src/domain/model/search/search-neighborhoods.model.ts b/src/microservice/domain/model/search/search-neighborhoods.model.ts similarity index 100% rename from src/domain/model/search/search-neighborhoods.model.ts rename to src/microservice/domain/model/search/search-neighborhoods.model.ts diff --git a/src/microservice/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts b/src/microservice/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts new file mode 100644 index 0000000..75faf7b --- /dev/null +++ b/src/microservice/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.ts @@ -0,0 +1,37 @@ +import { CheerioAPI } from 'cheerio'; +import { NeighborhoodsByCity } from '../../../model/neighborhoods-by-city.model'; +import { SearchNeighborhoods } from '../../../model/search/search-neighborhoods.model'; +import { PuppeteerRepository } from '../puppeteer.repository'; +import { IPuppeteerNeighborhoodRepository } from '../../../interface/puppeteer/repository/puppeteer-neighborhood-repository.interface'; +import { NotFoundException } from '../../../../../core/error-handling/exception/not-found.exception'; + +export abstract class PuppeteerNeighborhoodRepository + extends PuppeteerRepository + implements IPuppeteerNeighborhoodRepository +{ + async getNeighborhoodsByCity( + searchParams: SearchNeighborhoods + ): Promise { + this.validateInput(searchParams); + + const $ = await this.callEndpoint(searchParams); + const elements = this.buildElementsFromDocument(searchParams, $); + + this.validateOutput(elements); + + return elements; + } + + validateOutput(output: NeighborhoodsByCity[]): void { + if (output.length === 0) throw new NotFoundException('Neighborhoods'); + } + + abstract buildElementsFromDocument( + _searchParams: SearchNeighborhoods, + _$: CheerioAPI + ); + + abstract callEndpoint( + _searchParams: SearchNeighborhoods + ): Promise; +} diff --git a/src/domain/repository/puppeteer/puppeteer.repository.ts b/src/microservice/domain/repository/puppeteer/puppeteer.repository.ts similarity index 69% rename from src/domain/repository/puppeteer/puppeteer.repository.ts rename to src/microservice/domain/repository/puppeteer/puppeteer.repository.ts index 9309625..7408986 100644 --- a/src/domain/repository/puppeteer/puppeteer.repository.ts +++ b/src/microservice/domain/repository/puppeteer/puppeteer.repository.ts @@ -1,6 +1,7 @@ import * as cheerio from 'cheerio'; import { CheerioAPI } from 'cheerio'; -import { Page } from '../../interfaces/puppeteer/page.interface'; +import { Page } from '../../interface/puppeteer/page.interface'; +import { SearchNeighborhoods } from '../../model/search/search-neighborhoods.model'; export abstract class PuppeteerRepository { constructor(protected url: string, protected readonly page: Page) {} @@ -21,4 +22,8 @@ export abstract class PuppeteerRepository { /* istanbul ignore next */ return this.page.evaluate(() => document.querySelector('*').outerHTML); } + + validateInput(searchParams: SearchNeighborhoods) { + searchParams.validateIsAnyEmptyKey(); + } } diff --git a/src/domain/service/neighborhoods-service.interface.ts b/src/microservice/domain/service/neighborhoods-service.interface.ts similarity index 100% rename from src/domain/service/neighborhoods-service.interface.ts rename to src/microservice/domain/service/neighborhoods-service.interface.ts diff --git a/test/e2e/app.e2e-spec.ts b/test/e2e/app.e2e-spec.ts index ff9319b..531e271 100644 --- a/test/e2e/app.e2e-spec.ts +++ b/test/e2e/app.e2e-spec.ts @@ -1,8 +1,8 @@ -import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import * as request from 'supertest'; -// import expect from 'chai'; +import { expect } from 'chai'; import { AppModule } from '../../src/app.module'; +import { NestFactory } from '@nestjs/core'; jest.useFakeTimers(); jest.setTimeout(50000); @@ -11,12 +11,8 @@ describe('AppController (e2e)', () => { let app: INestApplication; beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule] - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); + app = await NestFactory.create(AppModule); + app.init(); }); afterEach(async () => { @@ -24,12 +20,10 @@ describe('AppController (e2e)', () => { }); it('/neighborhoods/city/brasil/sc/orleans (GET)', async () => { - try { - await request(app.getHttpServer()) - .get('/neighborhoods/city/brasil/sc/orleans') - .expect(200); - } catch (error) { - console.error(error); - } + const actual = await request(app.getHttpServer()) + .get('/neighborhoods/city/brasil/sc/orleans') + .expect(200); + + expect(actual.body).to.be.an('array').that.is.not.empty; }); }); diff --git a/test/unit/adapter/neighborhoods.module.spec.ts b/test/unit/adapter/neighborhoods.module.spec.ts deleted file mode 100644 index df80519..0000000 --- a/test/unit/adapter/neighborhoods.module.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from '../../../src/app.module'; -import { INestApplication } from '@nestjs/common/interfaces/nest-application.interface'; -import { expect } from 'chai'; -import * as request from 'supertest'; - -describe('NewStageForEmptyMovesInterfaceModule', () => { - let application: INestApplication; - - beforeEach(async function () { - application = await NestFactory.create(AppModule); - application.init(); - }); - - afterEach(async function () { - application.close(); - }); - - describe('#endpoint /', function () { - describe('GET /', function () { - it('should verify if default service returns 404', function (done) { - request(application.getHttpServer()) - .get('/') - .end(function (err, res) { - expect(res.status).to.be.equal(404); - done(err); - }); - }); - }); - }); -}); diff --git a/test/unit/core/error-handling/exception/custom-error.exception.spec.ts b/test/unit/core/error-handling/exception/custom-error.exception.spec.ts new file mode 100644 index 0000000..28593a8 --- /dev/null +++ b/test/unit/core/error-handling/exception/custom-error.exception.spec.ts @@ -0,0 +1,12 @@ +import '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { expect } from 'chai'; +import { CustomErrorException } from '../../../../../src/core/error-handling/exception/custom-error.exception'; + +describe('EmptyDataException ', () => { + it('Should call instanciate EmptyDataException correctly', function () { + const exception = new CustomErrorException('any', 500); + expect(exception.message).to.be.equal('any'); + expect(exception.getStatus()).to.be.equal(500); + expect(exception.errCode).to.be.equal(-1); + }); +}); diff --git a/test/unit/core/error-handling/exception/empty-data.exception.spec.ts b/test/unit/core/error-handling/exception/empty-data.exception.spec.ts new file mode 100644 index 0000000..e755fc9 --- /dev/null +++ b/test/unit/core/error-handling/exception/empty-data.exception.spec.ts @@ -0,0 +1,20 @@ +import '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { expect } from 'chai'; +import { EmptyDataException } from '../../../../../src/core/error-handling/exception/empty-data.exception'; +import { HttpStatus } from '@nestjs/common'; + +describe('EmptyDataException ', () => { + it('Should call instanciate EmptyDataException correctly', function () { + const exception = new EmptyDataException('any'); + expect(exception.message).to.be.equal('The any data cannot be empty'); + expect(exception.getStatus()).to.be.equal(HttpStatus.INTERNAL_SERVER_ERROR); + expect(exception.errCode).to.be.equal(1); + }); + + it('Should call instanciate EmptyDataException correctly with default param', function () { + const exception = new EmptyDataException(); + expect(exception.message).to.be.equal('The data cannot be empty'); + expect(exception.getStatus()).to.be.equal(HttpStatus.INTERNAL_SERVER_ERROR); + expect(exception.errCode).to.be.equal(1); + }); +}); diff --git a/test/unit/core/error-handling/filter/custom-error-exception.filter.spec.ts b/test/unit/core/error-handling/filter/custom-error-exception.filter.spec.ts new file mode 100644 index 0000000..6123510 --- /dev/null +++ b/test/unit/core/error-handling/filter/custom-error-exception.filter.spec.ts @@ -0,0 +1,115 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ExtensionsModule } from '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { expect } from 'chai'; +import { FiltersModule } from '../../../../../src/core/error-handling/filters.module'; +import { HttpStatus, INestApplication } from '@nestjs/common'; +import { CustomErrorExceptionFilter } from '../../../../../src/core/error-handling/filter/custom-error-exception.filter'; +import { EmptyPropException } from '../../../../../src/core/error-handling/exception/empty-prop.exception'; +import { createMock } from '@golevelup/ts-jest'; +import { ExecutionContext } from '@nestjs/common'; +import * as sinon from 'sinon'; +import { NestFactory } from '@nestjs/core'; +import { AppModule } from '../../../../../src/app.module'; + +describe('CustomErrorExceptionFilter', () => { + let sut: CustomErrorExceptionFilter; + let app: TestingModule; + let server: INestApplication; + + beforeEach(async () => { + app = await Test.createTestingModule({ + imports: [ExtensionsModule, FiltersModule], + controllers: [], + providers: [CustomErrorExceptionFilter] + }).compile(); + + sut = app.get(CustomErrorExceptionFilter); + server = await NestFactory.create(AppModule); + await server.init(); + }); + + afterEach(async () => { + await app.close(); + await server.close(); + }); + + describe('makeCustomResponse', () => { + it('should call makeCustomResponse and return the correct response', async () => { + const mockException = new EmptyPropException('any_prop'); + const mockResponse = { + status: HttpStatus.NOT_ACCEPTABLE, + message: `The property 'Any_prop' cannot be empty`, + type: 'EmptyPropException', + errorCode: 2 + }; + const actual = await sut.makeCustomResponse(mockException); + expect(JSON.stringify(actual)).to.be.equal(JSON.stringify(mockResponse)); + }); + }); + + describe('getResponse', () => { + it('should call getResponse and call the functions correctly', async () => { + const mockResponse = { + status: HttpStatus.NOT_ACCEPTABLE, + message: `The property 'Any_prop' cannot be empty`, + type: 'EmptyPropException', + errorCode: 2 + }; + + const mockArgHostResponse = createMock({ + switchToHttp: () => ({ + getResponse: () => mockResponse + }) + }); + + const mockException = new EmptyPropException('any_prop'); + + const actual = await sut.getResponse(mockArgHostResponse, mockException); + expect(JSON.stringify(actual)).to.be.equal(JSON.stringify(mockResponse)); + }); + }); + + describe('catch', () => { + it('should call catch and call the functions correctly', async () => { + const mockException = new EmptyPropException('any_prop'); + + const mockResponse = { + status: HttpStatus.NOT_ACCEPTABLE, + message: `The property 'Any_prop' cannot be empty`, + type: 'EmptyPropException', + errorCode: 2 + }; + + const mockAdapterResponse = { + status: () => HttpStatus.NOT_ACCEPTABLE, + message: `The property 'Any_prop' cannot be empty`, + type: 'EmptyPropException', + errorCode: 2, + json: () => mockResponse + }; + + const mockArgHostResponse = createMock({ + switchToHttp: () => ({ + getResponse: () => mockAdapterResponse + }) + }); + + const getResponseSpy = sinon.spy(sut, 'getResponse'); + const makeCustomResponseSpy = sinon.spy(sut, 'makeCustomResponse'); + sinon.stub(sut, 'httpAdapter').value(server.getHttpAdapter()); + + await sut.catch(mockException, mockArgHostResponse); + + sinon.assert.calledOnceWithExactly( + getResponseSpy, + mockArgHostResponse, + mockException + ); + + sinon.assert.calledOnceWithExactly(makeCustomResponseSpy, mockException); + + getResponseSpy.restore(); + makeCustomResponseSpy.restore(); + }); + }); +}); diff --git a/test/unit/core/error-handling/filter/error-exception.filter.spec.ts b/test/unit/core/error-handling/filter/error-exception.filter.spec.ts new file mode 100644 index 0000000..96d1e85 --- /dev/null +++ b/test/unit/core/error-handling/filter/error-exception.filter.spec.ts @@ -0,0 +1,78 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ExtensionsModule } from '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { expect } from 'chai'; +import { FiltersModule } from '../../../../../src/core/error-handling/filters.module'; +import { HttpStatus, NotFoundException } from '@nestjs/common'; +import { ErrorExceptionFilter } from '../../../../../src/core/error-handling/filter/error-exception.filter'; +import { CustomErrorException } from '../../../../../src/core/error-handling/exception/custom-error.exception'; + +describe('ErrorExceptionFilter', () => { + let sut: ErrorExceptionFilter; + let app: TestingModule; + + beforeEach(async () => { + app = await Test.createTestingModule({ + imports: [ExtensionsModule, FiltersModule], + controllers: [], + providers: [ErrorExceptionFilter] + }).compile(); + + sut = app.get(ErrorExceptionFilter); + }); + + afterEach(async () => { + await app.close(); + }); + + describe('makeCustomResponse', () => { + it('should call makeCustomResponse and return the correct response', async () => { + const mockException = new Error('any_error'); + const mockResponse = { + status: HttpStatus.INTERNAL_SERVER_ERROR, + message: `any_error`, + type: mockException.name, + errorCode: -1 + }; + const actual = await sut.makeCustomResponse(mockException); + expect(JSON.stringify(actual)).to.be.equal(JSON.stringify(mockResponse)); + }); + }); + + describe('getStatus', () => { + it('should call getStatus and return the correct value for error instance', async () => { + const mockException = new Error('any_error'); + const actual = await sut.getStatus(mockException); + expect(actual).to.be.equal(HttpStatus.INTERNAL_SERVER_ERROR); + }); + + it('should call getStatus and return the correct value for HttpException instance', async () => { + const mockException = new NotFoundException(); + const actual = await sut.getStatus(mockException); + expect(actual).to.be.equal(HttpStatus.NOT_FOUND); + }); + }); + + describe('getErrCode', () => { + it('should call getErrCode and return the correct value for error instance', async () => { + const mockException = new Error('any_error'); + const actual = await sut.getErrCode(mockException); + expect(actual).to.be.equal(-1); + }); + + it('should call getErrCode and return the correct value for CustomErrorException instance', async () => { + const mockException = new CustomErrorException( + 'any', + HttpStatus.INTERNAL_SERVER_ERROR, + 10 + ); + const actual = await sut.getErrCode(mockException); + expect(actual).to.be.equal(10); + }); + + it('should call getErrCode and return the correct value for HttpException instance', async () => { + const mockException = new NotFoundException(); + const actual = await sut.getErrCode(mockException); + expect(actual).to.be.equal(HttpStatus.NOT_FOUND); + }); + }); +}); diff --git a/test/unit/core/error-handling/filter/http-exception.filter.spec.ts b/test/unit/core/error-handling/filter/http-exception.filter.spec.ts new file mode 100644 index 0000000..1ef26eb --- /dev/null +++ b/test/unit/core/error-handling/filter/http-exception.filter.spec.ts @@ -0,0 +1,52 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { ExtensionsModule } from '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { expect } from 'chai'; +import { FiltersModule } from '../../../../../src/core/error-handling/filters.module'; +import { HttpExceptionFilter } from '../../../../../src/core/error-handling/filter/http-exception.filter'; +import { HttpStatus, NotFoundException } from '@nestjs/common'; + +describe('HttpExceptionFilter', () => { + let sut: HttpExceptionFilter; + let app: TestingModule; + + beforeEach(async () => { + app = await Test.createTestingModule({ + imports: [ExtensionsModule, FiltersModule], + controllers: [], + providers: [HttpExceptionFilter] + }).compile(); + + sut = app.get(HttpExceptionFilter); + }); + + afterEach(async () => { + await app.close(); + }); + + describe('getResponse', () => { + it('should call getResponse and return the correct response', async () => { + const mockException = new NotFoundException('http'); + const mockResponse = { + statusCode: HttpStatus.NOT_FOUND, + message: 'http', + error: 'Not Found' + }; + const actual = await sut.getResponse(null, mockException); + expect(JSON.stringify(actual)).to.be.equal(JSON.stringify(mockResponse)); + }); + }); + + describe('makeCustomResponse', () => { + it('should call makeCustomResponse and return the correct response', async () => { + const mockException = new NotFoundException('http'); + const mockResponse = { + status: HttpStatus.NOT_FOUND, + message: 'http', + type: 'NotFoundException', + errorCode: HttpStatus.NOT_FOUND + }; + const actual = await sut.makeCustomResponse(mockException); + expect(JSON.stringify(actual)).to.be.equal(JSON.stringify(mockResponse)); + }); + }); +}); diff --git a/test/unit/core/http/nest-response.builder.spec.ts b/test/unit/core/http/nest-response.builder.spec.ts new file mode 100644 index 0000000..b19cdda --- /dev/null +++ b/test/unit/core/http/nest-response.builder.spec.ts @@ -0,0 +1,12 @@ +import '../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { expect } from 'chai'; +import { NestResponseBuilder } from '../../../../src/core/http/nest-response.builder'; + +describe('NestResponseBuilder ', () => { + it('Should call instanciate NestResponseBuilder and set header correctly', function () { + const mockHeaders = new Object({ anyKey: 'anyHeader' }); + const nestBuilder = new NestResponseBuilder(); + const actual = nestBuilder.setHeader(mockHeaders).build(); + expect(actual.headers).to.be.equal(mockHeaders); + }); +}); diff --git a/test/unit/core/http/transform-response.interceptor.spec.ts b/test/unit/core/http/transform-response.interceptor.spec.ts new file mode 100644 index 0000000..19a5d9b --- /dev/null +++ b/test/unit/core/http/transform-response.interceptor.spec.ts @@ -0,0 +1,92 @@ +import '../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { expect } from 'chai'; +import { TransformResponseInterceptor } from '../../../../src/core/http/transform-response.interceptor'; +import { + ExecutionContext, + HttpStatus, + INestApplication, + ValidationPipe +} from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import { AppModule } from '../../../../src/app.module'; +import { createMock } from '@golevelup/ts-jest'; +import { NestResponse } from '../../../../src/core/http/nest-response'; +import { NestResponseBuilder } from '../../../../src/core/http/nest-response.builder'; +import { CustomResponse } from '../../../../src/core/interface/custom-response.interface'; +import { of } from 'rxjs'; +import * as sinon from 'sinon'; + +describe('TransformResponseInterceptor ', () => { + let app: INestApplication; + let mockAdapter; + + const callHandler = { + handle: jest.fn(() => of([mockNestResponse()])) + }; + + beforeEach(async function () { + app = await NestFactory.create(AppModule); + app.useGlobalPipes( + new ValidationPipe({ + transform: true + }) + ); + app.init(); + mockAdapter = { + httpAdapter: await app.getHttpAdapter() + }; + }); + + afterEach(async function () { + await app.close(); + }); + + const mockCustomResponse: CustomResponse = { + success: true, + response: 'any_data' + }; + + const mockNestResponse = (): NestResponse => { + const builder = new NestResponseBuilder(); + builder.setStatus(HttpStatus.OK); + builder.setBody(mockCustomResponse); + builder.setHeader({ + accept: 'application/json' + }); + return builder.build(); + }; + + describe('intercept', function () { + it('Should call instanciate TransformResponseInterceptor correctly', async function () { + const mockExecutionContext = createMock(); + + const sut = new TransformResponseInterceptor(mockAdapter); + + const actual = await sut.intercept(mockExecutionContext, callHandler); + + expect(typeof actual).to.be.equal('object'); + }); + }); + + describe('interceptResponse', function () { + it('Should call instanciate TransformResponseInterceptor correctly', async function () { + const mockExecutionContext = createMock(); + const sut = new TransformResponseInterceptor(mockAdapter); + const adapterStatusSpy = sinon.spy(mockAdapter.httpAdapter, 'status'); + const adapterHeaderSpy = sinon.spy(mockAdapter.httpAdapter, 'setHeader'); + + const actual = await sut.interceptResponse( + mockNestResponse(), + mockExecutionContext + ); + + sinon.assert.calledOnce(adapterStatusSpy); + sinon.assert.calledOnce(adapterHeaderSpy); + + expect(actual).to.be.equal(mockNestResponse().body); + + adapterHeaderSpy.restore(); + adapterStatusSpy.restore(); + }); + }); +}); diff --git a/test/unit/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.spec.ts b/test/unit/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.spec.ts deleted file mode 100644 index 7dab464..0000000 --- a/test/unit/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository.spec.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { PuppeteerModule } from 'nest-puppeteer'; -import { SearchNeighborhoods } from '../../../../../../src/domain/model/search/search-neighborhoods.model'; -import { ExtensionsModule } from '../../../../../../src/adapter/helper/extensions/exensions.module'; -import { PuppeteerNeighborhoodRepository } from '../../../../../../src/domain/repository/puppeteer/neighborhood/puppeteer-neighborhood.repository'; -import { expect } from 'chai'; -import * as sinon from 'sinon'; - -jest.useFakeTimers(); -jest.setTimeout(50000); - -class mockExtendClass extends PuppeteerNeighborhoodRepository {} - -describe('PuppeteerNeighborhoodRepository', () => { - let sut: PuppeteerNeighborhoodRepository; - let app: TestingModule; - - beforeEach(async () => { - app = await Test.createTestingModule({ - imports: [ - ExtensionsModule, - PuppeteerModule.forRoot({ - isGlobal: true - }) - ], - controllers: [], - providers: [mockExtendClass] - }).compile(); - - sut = app.get(mockExtendClass); - }); - - afterEach(async () => { - await app.close(); - }); - - describe('buildElementFromDocument', () => { - it('should call getEndpoint and throws a error', async () => { - const mockSearch = new SearchNeighborhoods('brasil', 'se', 'aracaju'); - try { - await sut.callEndpoint(mockSearch); - } catch (err) { - expect(err.message).to.be.equal('Method not implemented.'); - } - }); - - it('should call buildElementFromDocument and throws a error', async () => { - const mockSearch = new SearchNeighborhoods('brasil', 'se', 'aracaju'); - try { - await sut.buildElementFromDocument(mockSearch, null); - } catch (err) { - expect(err.message).to.be.equal('Method not implemented.'); - } - }); - - it('should call buildElementFromDocument and throws a error Method not implemented.', async () => { - const mockSearch = new SearchNeighborhoods('brasil', 'se', 'aracaju'); - const callback = function () { - return sut.buildElementFromDocument(mockSearch, null); - }; - expect(callback).to.throws(Error); - }); - }); - - describe('getNeighborhoodsByCity', () => { - it('should call getNeighborhoodsByCity and call callEndpoint with the correct params', async () => { - const callEndpointStub = sinon.stub(sut, 'callEndpoint').returns(null); - const buildElementFromDocumentStub = sinon - .stub(sut, 'buildElementFromDocument') - .returns([]); - - const mockSearch = new SearchNeighborhoods('brasil', 'se', 'aracaju'); - - await sut.getNeighborhoodsByCity(mockSearch); - - sinon.assert.calledOnceWithExactly(callEndpointStub, mockSearch); - - callEndpointStub.restore(); - buildElementFromDocumentStub.restore(); - }); - }); -}); diff --git a/test/unit/adapter/controller/neighborhoods.controller.spec.ts b/test/unit/microservice/adapter/controller/neighborhoods.controller.spec.ts similarity index 60% rename from test/unit/adapter/controller/neighborhoods.controller.spec.ts rename to test/unit/microservice/adapter/controller/neighborhoods.controller.spec.ts index e9c2f10..c7e3ccd 100644 --- a/test/unit/adapter/controller/neighborhoods.controller.spec.ts +++ b/test/unit/microservice/adapter/controller/neighborhoods.controller.spec.ts @@ -1,16 +1,17 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { NeighborhoodsController } from '../../../../src/adapter/controller/neighborhoods.controller'; -import { NeighborhoodsService } from '../../../../src/adapter/service/neighborhoods.service'; +import { NeighborhoodsController } from '../../../../../src/microservice/adapter/controller/neighborhoods.controller'; import { ConfigModule } from '@nestjs/config'; -import configuration from '../../../../src/config/configuration'; +import configuration from '../../../../../src/config/configuration'; import { expect } from 'chai'; import * as sinon from 'sinon'; -import { NeighborhoodsByCity } from '../../../../src/domain/model/neighborhoods-by-city.model'; +import { NeighborhoodsByCity } from '../../../../../src/microservice/domain/model/neighborhoods-by-city.model'; +import { ExtensionsModule } from '../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { NeighborhoodsService } from '../../../../../src/microservice/adapter/service/neighborhoods.service'; describe('NeighborhoodsController', () => { let neighborhoodsController: NeighborhoodsController; - const mockGuiaMaisRepository = { + const mockNeighborhoodsService = { getNeighborhoodsByCity: () => { return; } @@ -33,15 +34,15 @@ describe('NeighborhoodsController', () => { ConfigModule.forRoot({ isGlobal: true, load: [configuration] - }) + }), + ExtensionsModule ], controllers: [NeighborhoodsController], providers: [ { - provide: 'GuiaMaisRepository', - useValue: mockGuiaMaisRepository - }, - NeighborhoodsService + provide: NeighborhoodsService, + useFactory: () => mockNeighborhoodsService + } ] }).compile(); @@ -53,7 +54,7 @@ describe('NeighborhoodsController', () => { describe('NeighborhoodsController', () => { it('should call getNeighborhoodsByCity and return an array', async () => { const guiaMaisStub = sinon - .stub(mockGuiaMaisRepository, 'getNeighborhoodsByCity') + .stub(mockNeighborhoodsService, 'getNeighborhoodsByCity') .returns(mockNeighborhoods); const actual = await neighborhoodsController.getNeighborhoodsByCity( @@ -62,8 +63,8 @@ describe('NeighborhoodsController', () => { 'orleans' ); - expect(actual).to.be.an('array'); - expect(actual.length).to.be.equal(2); + expect(actual.body).to.be.an('array').that.contains; + expect(actual.body).to.have.lengthOf(2); guiaMaisStub.restore(); }); diff --git a/test/unit/microservice/adapter/helper/extensions/object.extension.spec.ts b/test/unit/microservice/adapter/helper/extensions/object.extension.spec.ts new file mode 100644 index 0000000..f7a0854 --- /dev/null +++ b/test/unit/microservice/adapter/helper/extensions/object.extension.spec.ts @@ -0,0 +1,29 @@ +import '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; +import { expect } from 'chai'; +import { SearchNeighborhoods } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods.model'; +import { EmptyPropException } from '../../../../../../src/core/error-handling/exception/empty-prop.exception'; + +describe('object.extension', () => { + describe('getMethods', function () { + it('Should call getMethods and contain toString', function () { + const obj = new Object(); + expect(obj.getMethods()).to.include.members(['toString']); + }); + }); + + describe('validateIsAnyEmptyKey', function () { + it('Should call validateIsAnyEmptyKey and throw exccption', function () { + const validation = () => { + const obj = new SearchNeighborhoods('brasil', 'sc', 'orleans'); + obj.city = ''; + obj.validateIsAnyEmptyKey(); + }; + + try { + validation(); + } catch (err) { + expect(err.message).to.be.equal("The property 'City' cannot be empty"); + } + }); + }); +}); diff --git a/test/unit/microservice/adapter/neighborhoods.module.spec.ts b/test/unit/microservice/adapter/neighborhoods.module.spec.ts new file mode 100644 index 0000000..8d71f6a --- /dev/null +++ b/test/unit/microservice/adapter/neighborhoods.module.spec.ts @@ -0,0 +1,35 @@ +import { HttpStatus } from '@nestjs/common'; +import { NeighborhoodsModule } from '../../../../src/microservice/adapter/neighborhoods.module'; +import { Test, TestingModule } from '@nestjs/testing'; +import { NeighborhoodsController } from '../../../../src/microservice/adapter/controller/neighborhoods.controller'; +import { ExtensionsModule } from '../../../../src/microservice/adapter/helper/extensions/exensions.module'; + +describe('NeighborhoodsModule', () => { + let sut: NeighborhoodsController; + let app: TestingModule; + + const mockGuiaMaisRepository = { + getNeighborhoodsByCity() { + return; + } + }; + + beforeEach(async function () { + app = await Test.createTestingModule({ + imports: [NeighborhoodsModule, ExtensionsModule], + providers: [] + }) + .overrideProvider('GuiaMaisRepository') + .useValue(mockGuiaMaisRepository) + .compile(); + + sut = app.get(NeighborhoodsController); + }); + + describe('NeighborhoodsController', function () { + it('should call buildResponse for status 200', async function () { + const actual = await sut.buildResponse(HttpStatus.OK, {}); + expect(actual.status).toBe(HttpStatus.OK); + }); + }); +}); diff --git a/test/unit/adapter/repository/neighborhoods/guia-mais.repository.spec.ts b/test/unit/microservice/adapter/repository/neighborhoods/guia-mais.repository.spec.ts similarity index 78% rename from test/unit/adapter/repository/neighborhoods/guia-mais.repository.spec.ts rename to test/unit/microservice/adapter/repository/neighborhoods/guia-mais.repository.spec.ts index 2d5ee21..3dbebae 100644 --- a/test/unit/adapter/repository/neighborhoods/guia-mais.repository.spec.ts +++ b/test/unit/microservice/adapter/repository/neighborhoods/guia-mais.repository.spec.ts @@ -2,11 +2,11 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ConfigService } from '@nestjs/config'; import { expect } from 'chai'; import * as sinon from 'sinon'; -import { GuiaMaisRepository } from '../../../../../src/adapter/repository/neighborhoods/guia-mais.repository'; +import { GuiaMaisRepository } from '../../../../../../src/microservice/adapter/repository/neighborhoods/guia-mais.repository'; import { PuppeteerModule } from 'nest-puppeteer'; -import { SearchNeighborhoods } from '../../../../../src/domain/model/search/search-neighborhoods.model'; +import { SearchNeighborhoods } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods.model'; import * as fs from 'fs'; -import { ExtensionsModule } from '../../../../../src/adapter/helper/extensions/exensions.module'; +import { ExtensionsModule } from '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; import * as cheerio from 'cheerio'; jest.useFakeTimers(); @@ -102,4 +102,14 @@ describe('GuiaMaisRepository', () => { goToUrlStub.restore(); }); }); + + describe('callEndpoint', () => { + it('should call callEndpoint and call getDocumentHtml with the correct params', async () => { + try { + sut.validateOutput([]); + } catch (err) { + expect(err.message).to.be.equal('Neighborhoods not found'); + } + }); + }); }); diff --git a/test/unit/adapter/service/neighborhoods.service.spec.ts b/test/unit/microservice/adapter/service/neighborhoods.service.spec.ts similarity index 82% rename from test/unit/adapter/service/neighborhoods.service.spec.ts rename to test/unit/microservice/adapter/service/neighborhoods.service.spec.ts index ede20db..5b5afb0 100644 --- a/test/unit/adapter/service/neighborhoods.service.spec.ts +++ b/test/unit/microservice/adapter/service/neighborhoods.service.spec.ts @@ -1,8 +1,8 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { NeighborhoodsService } from '../../../../src/adapter/service/neighborhoods.service'; +import { NeighborhoodsService } from '../../../../../src/microservice/adapter/service/neighborhoods.service'; import { expect } from 'chai'; import * as sinon from 'sinon'; -import { NeighborhoodsByCity } from '../../../../src/domain/model/neighborhoods-by-city.model'; +import { NeighborhoodsByCity } from '../../../../../src/microservice/domain/model/neighborhoods-by-city.model'; describe('NeighborhoodsService', () => { let sut: NeighborhoodsService; diff --git a/test/unit/domain/model/neighborhoods-by-city.model.spec.ts b/test/unit/microservice/domain/model/neighborhoods-by-city.model.spec.ts similarity index 75% rename from test/unit/domain/model/neighborhoods-by-city.model.spec.ts rename to test/unit/microservice/domain/model/neighborhoods-by-city.model.spec.ts index df084bb..9c6d8cf 100644 --- a/test/unit/domain/model/neighborhoods-by-city.model.spec.ts +++ b/test/unit/microservice/domain/model/neighborhoods-by-city.model.spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { NeighborhoodsByCity } from '../../../../src/domain/model/neighborhoods-by-city.model'; +import { NeighborhoodsByCity } from '../../../../../src/microservice/domain/model/neighborhoods-by-city.model'; describe('NeighborhoodsByCity', () => { it('should instance NeighborhoodsByCity and return the object with the correct properties', async () => { diff --git a/test/unit/domain/model/search/search-neighborhoods.model.spec.ts b/test/unit/microservice/domain/model/search/search-neighborhoods.model.spec.ts similarity index 75% rename from test/unit/domain/model/search/search-neighborhoods.model.spec.ts rename to test/unit/microservice/domain/model/search/search-neighborhoods.model.spec.ts index e4e67ea..815a4b6 100644 --- a/test/unit/domain/model/search/search-neighborhoods.model.spec.ts +++ b/test/unit/microservice/domain/model/search/search-neighborhoods.model.spec.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { SearchNeighborhoods } from '../../../../../src/domain/model/search/search-neighborhoods.model'; +import { SearchNeighborhoods } from '../../../../../../src/microservice/domain/model/search/search-neighborhoods.model'; describe('SearchNeighborhoods', () => { it('should instance SearchNeighborhoods and return the object with the correct properties', async () => { diff --git a/test/unit/domain/repository/puppeteer/puppeteer.repository.spec.ts b/test/unit/microservice/domain/repository/puppeteer/puppeteer.repository.spec.ts similarity index 83% rename from test/unit/domain/repository/puppeteer/puppeteer.repository.spec.ts rename to test/unit/microservice/domain/repository/puppeteer/puppeteer.repository.spec.ts index ec3f44b..c3806bc 100644 --- a/test/unit/domain/repository/puppeteer/puppeteer.repository.spec.ts +++ b/test/unit/microservice/domain/repository/puppeteer/puppeteer.repository.spec.ts @@ -5,10 +5,10 @@ import { EvaluateFn } from 'puppeteer'; import { OptionsPage, Page -} from '../../../../../src/domain/interfaces/puppeteer/page.interface'; -import { ExtensionsModule } from '../../../../../src/adapter/helper/extensions/exensions.module'; +} from '../../../../../../src/microservice/domain/interface/puppeteer/page.interface'; +import { ExtensionsModule } from '../../../../../../src/microservice/adapter/helper/extensions/exensions.module'; import { expect } from 'chai'; -import { PuppeteerRepository } from '../../../../../src/domain/repository/puppeteer/puppeteer.repository'; +import { PuppeteerRepository } from '../../../../../../src/microservice/domain/repository/puppeteer/puppeteer.repository'; jest.useFakeTimers(); jest.setTimeout(50000);