diff --git a/.commitlintrc b/.commitlintrc new file mode 100644 index 00000000..0df1d253 --- /dev/null +++ b/.commitlintrc @@ -0,0 +1,5 @@ +{ + "extends": [ + "@commitlint/config-conventional" + ] +} diff --git a/.eslintignore b/.eslintignore index 4203551b..a4ace9c6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ dist/ +**/dist/**/* node_modules/ coverage/ target/ @@ -12,5 +13,4 @@ target/ *.sublime-* *.swo *.swp -*.tgz -packages/bautajs-core/benchmark/legacy/*.js \ No newline at end of file +*.tgz \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index e0702a91..45a922fb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,27 +1,36 @@ { + "root": true, + "plugins": ["@typescript-eslint", "import", "prettier"], "extends": [ - "airbnb-base", "airbnb-typescript/base", - "plugin:jest/all", - "plugin:prettier/recommended", - "prettier" + "prettier", + "plugin:@typescript-eslint/recommended", + "plugin:import/typescript" ], "parser": "@typescript-eslint/parser", "parserOptions": { - "ecmaVersion": 2020, - "project": "./tsconfig.base.json", - "sourceType": "module" + "project": "tsconfig.eslint.json" }, "overrides": [ { - "files": "**/__tests__/*.js", + "files": "**/*.js", "rules": { - "example-internal/no-focused-tests": 2 + "@typescript-eslint/ban-ts-comment": 0, + "@typescript-eslint/no-var-requires": 0, + "import/extensions": 0 + } + }, + { + "files": "**/test/*", + "rules": { + "@typescript-eslint/ban-ts-comment": 0, + "@typescript-eslint/no-var-requires": 0 } } ], - "plugins": ["import", "prettier", "jest"], "rules": { + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/ban-ts-comment": 1, "@typescript-eslint/member-delimiter-style": [ "error", { @@ -40,11 +49,6 @@ "comma-dangle": ["error", "never"], "import/no-extraneous-dependencies": "off", "import/prefer-default-export": "off", - "jest/consistent-test-it": 0, - "jest/no-hooks": 0, - "jest/require-hook": 0, - "jest/prefer-expect-assertions": 0, - "jest/no-conditional-in-test": 0, "no-multiple-empty-lines": [ "error", { @@ -61,6 +65,16 @@ } ], "no-promise-executor-return": 0, - "class-methods-use-this": "warn" + "class-methods-use-this": "warn", + "import/extensions": [ + "error", + "always", + { + "pattern": { + "js": "never", + "ts": "never" + } + } + ] } } diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml deleted file mode 100644 index 55f81026..00000000 --- a/.github/workflows/ci-windows.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: CI - -on: workflow_dispatch - -jobs: - build: - name: CI - runs-on: windows-latest - - strategy: - fail-fast: false - matrix: - node-version: [14.x, 16.x, 18.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - run: npm audit --production --audit-level=high - - run: npm run lint - - run: npm run build --if-present - - run: npm test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ddd0545..bcf36621 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [main, 3.x] + branches: [main] pull_request: - branches: [main, 3.x] + branches: [main] jobs: build: @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [14.x, 16.x, 18.x] + node-version: [16.x, 18.x] os: [macos-latest, ubuntu-latest, windows-latest] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cd322869..cc379d7d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,14 +9,14 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: 'CodeQL' on: push: - branches: [ "main", "3.x" ] + branches: ['main'] pull_request: # The branches below must be a subset of the branches above - branches: [ "main", "3.x" ] + branches: ['main'] jobs: analyze: @@ -30,41 +30,40 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] + language: ['javascript'] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v3 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/release-new.yml b/.github/workflows/release-new.yml deleted file mode 100644 index e0bf0354..00000000 --- a/.github/workflows/release-new.yml +++ /dev/null @@ -1,70 +0,0 @@ -# BAUTA.JS RELEASE PROCESS -# 1) on-demand workflow -# 1.1) input version semver (X.Y.Z) mandatory -# 1.2) input branch (main by default) -# 2) lerna publish x.y.z -# 2.1) git tag + commit --- chore(release): vX.Y.Z -# 2.2) NPM publish -# 3) github release from tag vX.Y.Z on 1.2) branch input - -name: Bauta.js Release - -on: - workflow_dispatch: - inputs: - version: - description: 'Version (semver) to release' - required: true - type: string - branch: - description: 'Base branch to release' - required: true - default: 'main' - type: string - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ inputs.branch }} - fetch-depth: 0 - - uses: actions/setup-node@v3 - with: - node-version: 18 - registry-url: https://registry.npmjs.org/ - - name: Install dependencies - run: npm i - - name: Build distributable files - run: npm run build - - name: Run linter - run: npm run lint - - name: Run tests - run: npm test - - name: Removing package-lock - run: rm package-lock.json - - name: Versioning & Publishing - run: | - git config user.name "${{ github.actor }}" - git config user.email "${{ github.actor}}@users.noreply.github.com" - npm run release ${{ inputs.version }} - env: - NODE_AUTH_TOKEN: ${{secrets.npm_token}} - GITHUB_TOKEN: ${{ secrets.GH_CLI_TOKEN }} - - release: - needs: publish - if: | - always() && - needs.publish.result == 'success' - runs-on: 'ubuntu-latest' - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - name: 'Create GitHub release' - run: | - gh release create v${{ inputs.version }} --verify-tag --target ${{ inputs.branch}} -t 'v${{ inputs.version }}' --generate-notes - env: - GITHUB_TOKEN: ${{ secrets.GH_CLI_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bd0766b3..7410b757 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,24 +1,70 @@ -# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created -# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages +# BAUTA.JS RELEASE PROCESS +# 1) on-demand workflow +# 1.1) input version semver (X.Y.Z) mandatory +# 1.2) input branch (main by default) +# 2) lerna publish x.y.z +# 2.1) git tag + commit --- chore(release): vX.Y.Z +# 2.2) NPM publish +# 3) github release from tag vX.Y.Z on 1.2) branch input -name: Publish npm version when new GitHub release +name: Bauta.js Release on: - release: - types: [published] + workflow_dispatch: + inputs: + version: + description: 'Version (semver) to release' + required: true + type: string + branch: + description: 'Base branch to release' + required: true + default: 'main' + type: string jobs: - publish-npm: + publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v3 + with: + ref: ${{ inputs.branch }} + fetch-depth: 0 + - uses: actions/setup-node@v2 with: node-version: 18 registry-url: https://registry.npmjs.org/ - - run: npm i - - run: npm run build - - run: npm test - - run: npm run release:from-package + - name: Install dependencies + run: npm i + - name: Build distributable files + run: npm run build + - name: Run linter + run: npm run lint + - name: Run tests + run: npm test + - name: Removing package-lock + run: rm package-lock.json + - name: Versioning & Publishing + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + GH_TOKEN: ${{ secrets.GH_CLI_TOKEN }} + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor}}@users.noreply.github.com" + npm run release ${{ inputs.version }} + + release: + needs: publish + if: | + always() && + needs.publish.result == 'success' + runs-on: 'ubuntu-latest' + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: 'Create GitHub release' + run: | + gh release create v${{ inputs.version }} --verify-tag --target ${{ inputs.branch}} -t 'v${{ inputs.version }}' --generate-notes env: - NODE_AUTH_TOKEN: ${{secrets.npm_token}} + GITHUB_TOKEN: ${{ secrets.GH_CLI_TOKEN }} diff --git a/.gitignore b/.gitignore index 9b3a27bc..b463ecdc 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,7 @@ dist/ coverage/ # TypeScript incremental compilation cache -*.tsbuildinfo \ No newline at end of file +*.tsbuildinfo + +## Sonar reporter +test-report.xml diff --git a/.husky/commit-msg b/.husky/commit-msg index 314e8214..e8511eae 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npx --no-install commitlint --edit $1 \ No newline at end of file +npx --no-install commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit index cb34ea5b..61b4defb 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,5 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -npm run lint && npm test \ No newline at end of file +npm run lint +npm test \ No newline at end of file diff --git a/README.md b/README.md index 9955bb13..01e7b7e3 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ Bauta.js has a set of valuable decorators that you can use to build your resolve - [pairwise](./docs/decorators/pairwise.md) - [tap](./docs/decorators/tap.md) - [retryWhen](./docs/decorators/retry-when.md) +- [cache](./docs/decorators/cache.md) ### Request and response validation @@ -225,7 +226,6 @@ Bauta.js is a monorepo containing the following list of packages: - [BautaJS Core](./packages/bautajs-core) - [BautaJS Fastify](./packages/bautajs-fastify) - [BautaJS Express](./packages/bautajs-express) -- [BautaJS Cache Decorator](./packages/bautajs-decorator-cache) - [BautaJS Rest Datasource](./packages/bautajs-datasource-rest) ## Code of Conduct @@ -244,7 +244,6 @@ Licensed under the MIT License. - [BautaJS Core](./packages/bautajs-core/README.md#third-party-dependencies-licenses) - [BautaJS Fastify](./packages/bautajs-fastify/README.md#third-party-dependencies-licenses) - [BautaJS Express](./packages/bautajs-express/README.md#third-party-dependencies-licenses) -- [BautaJS Cache Decorator](./packages/bautajs-decorator-cache/README.md#third-party-dependencies-licenses) - [BautaJS Rest Datasource](./packages/bautajs-datasource-rest/README.md#third-party-dependencies-licenses) ### Development diff --git a/SECURITY.md b/SECURITY.md index b4fa2c52..9c8716d1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,20 +2,20 @@ ## Supported Versions -| Version | Supported | -| ------- | ------------------ | -| 1.x | :x: | -| 2.x | :white_check_mark: | +| Version | Supported | +| ---------------- |--------------------| +| Bauta.js 1.x | :x: | +| Bauta.js 2.x | :x: | +| Bauta.js 3.x | :white_check_mark: | Additionally, we only support the [LTS Node.js versions](https://nodejs.org/en/about/releases/). Bauta.js will deprecate the non-supported Node.js version following the Release process and timeline of Node.js. -| Version | Supported | -| ------- | ------------------- | -| 14.x | :white_check_mark: | -| 16.x | :white_check_mark: | -| 18.x | :white_check_mark: | +| Version | Supported | +| ---------------- | ------------------ | +| Node.js 16.x | :white_check_mark: | +| Node.js 18.x | :white_check_mark: | ## Reporting a Vulnerability diff --git a/packages/bautajs-decorator-cache/README.md b/docs/decorators/cache.md similarity index 76% rename from packages/bautajs-decorator-cache/README.md rename to docs/decorators/cache.md index 60f51fd2..f5494d87 100644 --- a/packages/bautajs-decorator-cache/README.md +++ b/docs/decorators/cache.md @@ -1,21 +1,13 @@ -## BautaJS cache decorator - -A cache decorator using [moize](https://github.com/planttheidea/moize) for Bauta.js pipelines. - -## How to install - -```console - npm install @axa/bautajs-decorator-cache -``` +## cache decorator +A cache decorator using [quick-lru](https://github.com/sindresorhus/quick-lru) for Bauta.js pipelines. ## Usage Include it on your pipeline as follows: ```js - import { pipe, createContext } from '@axa/bautajs-core'; - import { cache } from '@axa/bautajs-decorator-cache'; + import { pipe, createContext, cache } from '@axa/bautajs-core'; function createAKey(prev, ctx, bautajs) { ctx.data.myKey = 'mykey'; @@ -35,7 +27,7 @@ Include it on your pipeline as follows: doSomethingHeavy ); - const cacheMyPipeline = cache(myPipeline, (prev, ctx) => ctx.data.myKey, { maxSize:3 }); + const cacheMyPipeline = cache(myPipeline, { maxSize:3 }, (prev, ctx) => ctx.data.myKey); const result = await cacheMyPipeline(null, createContext({req:{}}), {}); console.log(result); @@ -48,16 +40,12 @@ Include it on your pipeline as follows: Normalize functions must return an identifier key in the form of a primitive type that is used to determine if a new value requires cache or not. If you need to use more than one field to generate a key, concatenate or stringify those fields that you need. -There are two main use cases in normalize: -- you want to use only fields from the context to generate the key -- you want to use at least one field from a previously generated object - ### Normalize with only context fields It is straightforward and you can do the following: ```js -const normalizer = (_, ctx) => ctx.whatever_field; +const normalizer = (_, ctx) => ctx.data.whatever_field; ``` ### Normalize uses at least one field from a previously generated object @@ -66,8 +54,7 @@ This is trickier because you have to take into account that you will not have th ```js - const { pipe } = require('@axa/bautajs-core'); - const { cache } = require('@axa/bautajs-decorator-cache'); + const { pipe, cache } = require('@axa/bautajs-core'); const { someHeavyOperation } = require('./my-helper'); const myPipeline = pipe( someHeavyOperation, (result) => ({...result, iWantToUseAsKeyThis:1})) @@ -77,9 +64,9 @@ This is trickier because you have to take into account that you will not have th operations.v1.op1.setup(p => p.pipe( cache( - myPipeline, - normalizer, // When normalizer is called, result from pipeline is not yet there - { maxSize:5 } + myPipeline, + { maxSize:5 }, + normalizer // When normalizer is called, result from pipeline is not yet there ) ) ); @@ -107,7 +94,7 @@ To use the object as a key in the cache normalizer, this object needs to be set .pipe((_, ctx) => { return { iAmTheKey: 'test' }; }, - cache(pp, normalizer, { maxSize: 5 }) + cache(pp, { maxSize: 5 }, normalizer) ); ``` diff --git a/docs/developer-experience/decorators.md b/docs/developer-experience/decorators.md new file mode 100644 index 00000000..3d13dfb7 --- /dev/null +++ b/docs/developer-experience/decorators.md @@ -0,0 +1,38 @@ +# Decorators list + +Below there is a list of decorators and what they accept based on the following icons: + +- :hourglass_flowing_sand: the hourglass icon means that input is a promise +- :bulb: the bulb icon means that input is a synchronous value or function + +# List of Bauta decorators + +| Allows | Name | path | +|---------------------------------|------------------------|-------------------------------------------------------------------------------------------------| +| :bulb: :hourglass_flowing_sand: | pipeline (inputs) | [bautajs-core/decorators/pipeline](../../packages/bautajs-core/src/decorators/pipeline.ts) | +| :bulb: | pipeline (catch-error) | | +| :bulb: | resolver | [bautajs-core/decorators/resolver](../../packages/bautajs-core/src/decorators/resolver.ts) | +| :bulb: :hourglass_flowing_sand: | step(*) | [bautajs-core/decorators/step](../../packages/bautajs-core/src/decorators/step.ts) | + + +# List of Utility decorators + + +| Allows | Name | path | +|---------------------------------|----------------------|--------------------------------------------------------------------------------------------------------------------| +| :bulb: | as-promise | [bautajs-core/decorators/as-promise](../../packages/bautajs-core/src/decorators/as-promise.ts) | +| :bulb: | as-value | [bautajs-core/decorators/as-value](../../packages/bautajs-core/src/decorators/as-value.ts) | +| :bulb: | cache | [bautajs-core/decorators/cache](../../packages/bautajs-core/src/decorators/cache.ts) | +| :bulb: :hourglass_flowing_sand: | iif(*) | [bautajs-core/decorators/iif](../../packages/bautajs-core/src/decorators/iif.ts) | +| :bulb: :hourglass_flowing_sand: | map(*) | [bautajs-core/decorators/map](../../packages/bautajs-core/src/decorators/map.ts) | +| :bulb: | match (matchers) | [bautajs-core/decorators/match](../../packages/bautajs-core/src/decorators/match.ts) | +| :bulb: :hourglass_flowing_sand: | match (pipelines) | | +| :bulb: :hourglass_flowing_sand: | pairwise | [bautajs-core/decorators/pairwise](../../packages/bautajs-core/src/decorators/pairwise.ts) | +| :hourglass_flowing_sand: | parallel-all-settled | [bautajs-core/decorators/parallel-all-settled](../../packages/bautajs-core/src/decorators/parallel-all-settled.ts) | +| :hourglass_flowing_sand: | parallel-map | [bautajs-core/decorators/parallel-map](../../packages/bautajs-core/src/decorators/parallel-map.ts) | +| :hourglass_flowing_sand: | parallel | [bautajs-core/decorators/parallel](../../packages/bautajs-core/src/decorators/parallel.ts) | +| :bulb: :hourglass_flowing_sand: | retry-when | [bautajs-core/decorators/retry-when](../../packages/bautajs-core/src/decorators/retry-when.ts) | +| :bulb: :hourglass_flowing_sand: | tap | [bautajs-core/decorators/tap](../../packages/bautajs-core/src/decorators/tap.ts) | + +(*): All these decorators are transparent to the fact that they are using promises or not. That is: if input is a value, they return a value, but if their input is a promise, they return a promise. + diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index c0817d08..00000000 --- a/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -const config = require('./jest.config.base'); - -module.exports = { displayName: 'bautajs', ...config, projects: ['/packages/*'] }; diff --git a/lerna.json b/lerna.json index fee5857b..520ace91 100644 --- a/lerna.json +++ b/lerna.json @@ -7,8 +7,12 @@ "bootstrap": { "npmClientArgs": [ "--no-package-lock" - ] + ], + "version": { + "message": "chore(release): %s" + } } }, - "version": "2.2.2" + "useWorkspaces": true, + "version": "3.0.0-alpha.2" } diff --git a/package.json b/package.json index 13d5d570..d3844b6e 100644 --- a/package.json +++ b/package.json @@ -12,89 +12,75 @@ "proxy", "multipart" ], + "workspaces": [ + "packages/*" + ], "license": "SEE LICENSE IN LICENSE.txt", "bugs": { "url": "https://github.com/axa-group/bauta.js/issues" }, "homepage": "https://github.com/axa-group/bauta.js#readme", + "engines": { + "node": ">=16" + }, + "scripts": { + "benchmark-core": "npx concurrently -k -s first \"node ./packages/bautajs-core/benchmark/simple.js\" \"npx autocannon -c 100 -d 5 -p 10 localhost:3000/api/op1\"", + "benchmark-express": "npx concurrently -k -s first \"node ./packages/bautajs-express/benchmark/simple.js\" \"npx autocannon -c 20 -d 5 -p 10 localhost:3000/api/op1\"", + "benchmark-fastify": "npx concurrently -k -s first \"node ./packages/bautajs-fastify/benchmark/simple.js\" \"npx autocannon -c 100 -d 5 -p 10 localhost:3000/api/op1\"", + "clean": "turbo run clean && rm -rf node_modules", + "lint": "eslint \"*/**/*.{ts,js}\"", + "test": "turbo run test", + "release": "lerna changed && lerna publish --no-changelog --yes --no-commit-hooks", + "checkDeps": "npx npm-check-updates -u && npm run lerna exec -- npx npm-check-updates -u", + "build": "turbo run build", + "prepare": "husky install" + }, "devDependencies": { - "@commitlint/config-conventional": "^17.0.2", + "@commitlint/cli": "^17.4.1", + "@commitlint/config-conventional": "^17.4.0", "@types/body-parser": "^1.19.2", "@types/compression": "^1.7.2", - "@types/cors": "^2.8.12", - "@types/express": "^4.17.13", + "@types/cors": "^2.8.13", + "@types/express": "^4.17.17", "@types/express-pino-logger": "^4.0.3", - "@types/jest": "^28.1.3", - "@types/node": "^18.0.0", + "@types/jest": "^29.5.0", + "@types/node": "^18.15.11", "@types/pino": "^7.0.5", "@types/split2": "^3.2.1", "@types/supertest": "^2.0.12", "@types/swagger-ui-express": "4.1.3", - "@typescript-eslint/eslint-plugin": "^5.29.0", - "@typescript-eslint/parser": "^5.29.0", - "bench": "^0.3.6", - "commitizen": "^4.2.4", - "commitlint": "^17.0.2", - "cz-conventional-changelog": "^3.3.0", - "eslint": "^8.18.0", - "eslint-config-airbnb-base": "^15.0.0", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "@typescript-eslint/parser": "^5.57.0", + "eslint": "^8.37.0", "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jest": "^26.5.3", - "eslint-plugin-prettier": "^4.0.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-prettier": "^4.2.1", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", "got": "^11.8.5", - "husky": "^8.0.1", - "jest": "^28.1.1", - "jest-config": "^28.1.1", - "jest-extended": "2.0.0", - "jest-junit": "13.2.0", + "husky": "^8.0.3", + "jest": "^29.5.0", + "jest-config": "^29.5.0", + "jest-extended": "^3.2.4", + "jest-junit": "^15.0.0", "jest-sonar-reporter": "^2.0.0", - "lerna": "^5.1.5", - "nock": "^13.2.7", - "node-mocks-http": "^1.11.0", - "pino": "^8.4.2", - "prettier": "^2.7.1", - "prettier-check": "^2.0.0", - "split2": "^4.1.0", + "lerna": "^6.6.1", + "nock": "^13.3.0", + "node-mocks-http": "^1.12.2", + "node-object-hash": "^2.3.10", + "pino": "^8.11.0", + "prettier": "^2.8.7", + "split2": "^4.2.0", "stream-mock": "^2.0.5", - "supertest": "^6.2.3", - "ts-jest": "^28.0.5", - "typescript": "4.9.x" - }, - "scripts": { - "lerna": "lerna", - "cm": "git-cz", - "benchmark-core": "npx concurrently -k -s first \"node ./packages/bautajs-core/benchmark/simple.js\" \"npx autocannon -c 100 -d 5 -p 10 localhost:3000/api/op1\"", - "benchmark-express": "npx concurrently -k -s first \"node ./packages/bautajs-express/benchmark/simple.js\" \"npx autocannon -c 20 -d 5 -p 10 localhost:3000/api/op1\"", - "benchmark-fastify": "npx concurrently -k -s first \"node ./packages/bautajs-fastify/benchmark/simple.js\" \"npx autocannon -c 100 -d 5 -p 10 localhost:3000/api/op1\"", - "lint": "eslint \"*/**/*.{ts,tsx}\"", - "bootstrap": "npm run lerna bootstrap -- --no-ci", - "prepare": "husky install && npm run lerna bootstrap -- --no-ci && npm run build", - "clean": "npm run lerna clean -- --yes && git clean -dfqX -- ./node_modules **/{dist,node_modules}/ ./packages/*/tsconfig*tsbuildinfo ./packages/*/package-lock.json ./coverage", - "release:local": "npm run clean && npm i && npm run test && lerna publish --exact --conventional-commits --no-verify-access --no-verify-registry --yes", - "release": "lerna publish --exact --conventional-commits --no-verify-access --no-verify-registry --yes", - "release:from-package": "lerna publish from-package --exact --no-verify-access --no-verify-registry --yes", - "test": "jest --verbose --detectOpenHandles", - "test:clean": "jest --clearCache", - "test:watch": "npm run test -- --watchAll", - "checkDeps": "npm run lerna exec -- --concurrency 14 -- npm outdated", - "build": "tsc --build tsconfig.build.json", - "build:clean": "tsc --build tsconfig.build.json --clean", - "watch": "tsc --build tsconfig.build.json --watch" - }, - "engines": { - "node": ">=14" - }, - "config": { - "commitizen": { - "path": "./node_modules/cz-conventional-changelog" - } + "supertest": "^6.3.3", + "ts-jest": "^29.1.0", + "turbo": "^1.9.3", + "typescript": "~5.0.0" }, "jestSonar": { "reportPath": "coverage", "reportFile": "test-reporter.xml" - } + }, + "packageManager": "npm@9.8.0" } diff --git a/packages/bautajs-core/jest.config.js b/packages/bautajs-core/jest.config.js index 341747ed..1aed5f9f 100644 --- a/packages/bautajs-core/jest.config.js +++ b/packages/bautajs-core/jest.config.js @@ -1,3 +1,6 @@ -const config = require('../../jest.config.base'); +const config = require('@axa/bautajs-dev-config/jest.config.base'); -module.exports = { displayName: '@axa/bautajs-core', ...config }; +module.exports = { + displayName: '@axa/bautajs-core', + ...config +}; diff --git a/packages/bautajs-core/package.json b/packages/bautajs-core/package.json index 59a9213c..890edb69 100644 --- a/packages/bautajs-core/package.json +++ b/packages/bautajs-core/package.json @@ -1,6 +1,6 @@ { "name": "@axa/bautajs-core", - "version": "2.2.2", + "version": "3.0.0-alpha.2", "description": "Bauta.js is an add-on for your Node.js applications such as Express.js or Fastify.", "main": "dist", "types": "dist", @@ -8,7 +8,10 @@ "dist" ], "scripts": { - "tsc": "tsc" + "tsc": "tsc", + "build": "tsc --build", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", + "test": "jest" }, "bugs": { "url": "https://github.com/axa-group/bauta.js/issues" @@ -28,18 +31,22 @@ ], "license": "SEE LICENSE IN LICENSE.txt", "engines": { - "node": ">=14", + "node": ">=16", "npm": ">=8" }, "dependencies": { "@apidevtools/swagger-parser": "^10.1.0", - "ajv": "^8.11.0", + "ajv": "^8.12.0", "ajv-formats": "^2.1.1", - "fast-glob": "^3.2.11", + "fast-glob": "^3.2.12", "fast-safe-stringify": "^2.1.1", - "hyperid": "^3.0.1", - "openapi-types": "^12.0.0", + "hyperid": "^3.1.1", + "openapi-types": "^12.1.0", "p-cancelable": "^2.1.1", - "pino": "^8.4.2" + "pino": "^8.11.0", + "quick-lru-cjs": "^5.2.1" + }, + "devDependencies": { + "@axa/bautajs-dev-config": "*" } } diff --git a/packages/bautajs-core/src/bauta.ts b/packages/bautajs-core/src/bauta.ts index eca07c47..6d651934 100644 --- a/packages/bautajs-core/src/bauta.ts +++ b/packages/bautajs-core/src/bauta.ts @@ -43,8 +43,10 @@ function prebuildApi(apiDefinition: Document): API { }) .flat(1) }; - } catch (e: any) { - throw new Error(`The OpenAPI API definition provided is not valid. Error ${e.message}`); + } catch (e) { + throw new Error( + `The OpenAPI API definition provided is not valid. Error ${(e as Error).message}` + ); } } @@ -83,7 +85,7 @@ export class BautaJS implements BautaJSInstance { public readonly validator: Validator; - private bootstrapped: boolean = false; + private bootstrapped = false; constructor({ apiDefinition, @@ -174,6 +176,7 @@ export class BautaJS implements BautaJSInstance { enableResponseValidation: boolean, api?: API ): Operations { + // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; function createOperation(operationId: string) { @@ -230,11 +233,11 @@ export class BautaJS implements BautaJSInstance { * * const files = requireAll('./my/path/to/datasources/*.js', true, {someVar:123}); */ - static requireAll(folder: string | string[], execute: boolean = true, vars?: T) { + static requireAll(folder: string | string[], execute = true, vars?: T) { const execFiles = (folderPath: string) => { const result: any = []; fastGlob.sync(folderPath.replace(/\\/g, '/')).forEach((file: string) => { - // eslint-disable-next-line global-require, import/no-dynamic-require + // eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires let data = require(resolve(file)); if (data.default) { data = data.default; diff --git a/packages/bautajs-core/src/core/cancelable-token.ts b/packages/bautajs-core/src/core/cancelable-token.ts index 0c72660f..e4d62634 100644 --- a/packages/bautajs-core/src/core/cancelable-token.ts +++ b/packages/bautajs-core/src/core/cancelable-token.ts @@ -3,7 +3,7 @@ import { CancelableToken, OnCancel } from '../types'; export class CancelableTokenBuilder implements CancelableToken { private cancelStack: OnCancel[] = []; - public isCanceled: boolean = false; + public isCanceled = false; cancel() { this.isCanceled = true; diff --git a/packages/bautajs-core/src/core/not-found-error.ts b/packages/bautajs-core/src/core/not-found-error.ts index e12dfe47..6992cfe6 100644 --- a/packages/bautajs-core/src/core/not-found-error.ts +++ b/packages/bautajs-core/src/core/not-found-error.ts @@ -3,7 +3,7 @@ import fastSafeStringify from 'fast-safe-stringify'; export class NotFoundError extends Error { public statusCode: number; - constructor(message: string, statusCode: number = 404) { + constructor(message: string, statusCode = 404) { super(message); this.name = 'Not Found Error'; this.statusCode = statusCode; diff --git a/packages/bautajs-core/src/core/operation.ts b/packages/bautajs-core/src/core/operation.ts index 1b221777..19c275b4 100755 --- a/packages/bautajs-core/src/core/operation.ts +++ b/packages/bautajs-core/src/core/operation.ts @@ -24,17 +24,17 @@ export class OperationBuilder implements Operation { public schema?: OpenAPI.Operation; - public deprecated: boolean = false; + public deprecated = false; - public requestValidationEnabled: boolean = true; + public requestValidationEnabled = true; - public responseValidationEnabled: boolean = false; + public responseValidationEnabled = false; public handler: Pipeline.StepFunction; private private?: boolean; - private setupDone: boolean = false; + private setupDone = false; private validator?: OperationValidators; diff --git a/packages/bautajs-core/src/core/validation-error.ts b/packages/bautajs-core/src/core/validation-error.ts index f77743a0..0422949e 100644 --- a/packages/bautajs-core/src/core/validation-error.ts +++ b/packages/bautajs-core/src/core/validation-error.ts @@ -8,7 +8,7 @@ export class ValidationError extends Error implements IValidationError { public response: any; - constructor(message: string, errors: LocationError[], statusCode: number = 500, response?: any) { + constructor(message: string, errors: LocationError[], statusCode = 500, response?: any) { super(message); this.name = 'Validation Error'; this.errors = errors; diff --git a/packages/bautajs-decorator-cache/src/index.ts b/packages/bautajs-core/src/decorators/cache.ts similarity index 88% rename from packages/bautajs-decorator-cache/src/index.ts rename to packages/bautajs-core/src/decorators/cache.ts index 97744d7a..f99574f4 100644 --- a/packages/bautajs-decorator-cache/src/index.ts +++ b/packages/bautajs-core/src/decorators/cache.ts @@ -1,7 +1,14 @@ -import { BautaJSInstance, Context, Pipeline } from '@axa/bautajs-core'; -import QuickLRU, { Options } from 'quick-lru-cjs'; import nodeObjectHash from 'node-object-hash'; -import { Normalizer, CacheStepFunction } from './types'; +import QuickLRU, { Options } from 'quick-lru-cjs'; +import { BautaJSInstance, Context, Pipeline } from '../types'; + +export interface Normalizer { + (prev: TIn, ctx: Context, bautajs: BautaJSInstance): CacheKey; +} + +export interface CacheStepFunction extends Pipeline.StepFunction { + store: QuickLRU; +} const objectHash = nodeObjectHash({ // We don't care about the order of an object properties this could add some overhead over the performance. @@ -24,8 +31,7 @@ const objectHash = nodeObjectHash({ * @param {Number} options.maxSize=500 Max number of items on cache. * @return {CacheDecoratorFunction} An operation function that you can plug in on a `bautajs` pipeline. * @example - * import { pipe, createContext } from '@axa/bautajs-core'; - * import { cache } from '@axa/bautajs-decorator-cache'; + * import { pipe, createContext, cache } from '@axa/bautajs-core'; * * function createAKey(prev, ctx, bautajs) { * ctx.data.myKey = 'mykey'; @@ -88,6 +94,3 @@ export function cache( writable: false }) as CacheStepFunction; } - -export default cache; -export * from './types'; diff --git a/packages/bautajs-core/src/decorators/match.ts b/packages/bautajs-core/src/decorators/match.ts index 753cca39..50e6f20c 100644 --- a/packages/bautajs-core/src/decorators/match.ts +++ b/packages/bautajs-core/src/decorators/match.ts @@ -39,6 +39,7 @@ class MatchBuilder implements Match { pipeline: Pipeline.StepFunction; }[] = []; + // eslint-disable-next-line class-methods-use-this private otherwisePipeline: Pipeline.StepFunction = () => null as any; run(value: TIn, ctx: Context, bautajs: BautaJSInstance) { diff --git a/packages/bautajs-core/src/decorators/pipeline.ts b/packages/bautajs-core/src/decorators/pipeline.ts index fd466848..5acce8c6 100644 --- a/packages/bautajs-core/src/decorators/pipeline.ts +++ b/packages/bautajs-core/src/decorators/pipeline.ts @@ -28,21 +28,25 @@ function compose( export function pipe( f1: Pipeline.StepFunction ): Pipeline.PipelineFunction; + export function pipe( f1: Pipeline.StepFunction, f2: Pipeline.StepFunction ): Pipeline.PipelineFunction; + export function pipe( f1: Pipeline.StepFunction, f2: Pipeline.StepFunction, f3: Pipeline.StepFunction ): Pipeline.PipelineFunction; + export function pipe( f1: Pipeline.StepFunction, f2: Pipeline.StepFunction, f3: Pipeline.StepFunction, f4: Pipeline.StepFunction ): Pipeline.PipelineFunction; + export function pipe( f1: Pipeline.StepFunction, f2: Pipeline.StepFunction, @@ -50,6 +54,7 @@ export function pipe, f5: Pipeline.StepFunction ): Pipeline.PipelineFunction; + export function pipe< ValueType, ResultValue1, @@ -66,6 +71,7 @@ export function pipe< f5: Pipeline.StepFunction, f6: Pipeline.StepFunction ): Pipeline.PipelineFunction; + export function pipe< ValueType, ResultValue1, @@ -84,6 +90,7 @@ export function pipe< f6: Pipeline.StepFunction, f7: Pipeline.StepFunction ): Pipeline.PipelineFunction; + export function pipe< ValueType, ResultValue1, @@ -104,6 +111,7 @@ export function pipe< f7: Pipeline.StepFunction, f8: Pipeline.StepFunction ): Pipeline.PipelineFunction; + export function pipe< ValueType, ResultValue1, @@ -127,6 +135,252 @@ export function pipe< f9: Pipeline.StepFunction ): Pipeline.PipelineFunction; +export function pipe< + ValueType, + ResultValue1, + ResultValue2, + ResultValue3, + ResultValue4, + ResultValue5, + ResultValue6, + ResultValue7, + ResultValue8, + ResultValue9, + ReturnType +>( + f1: Pipeline.StepFunction, + f2: Pipeline.StepFunction, + f3: Pipeline.StepFunction, + f4: Pipeline.StepFunction, + f5: Pipeline.StepFunction, + f6: Pipeline.StepFunction, + f7: Pipeline.StepFunction, + f8: Pipeline.StepFunction, + f9: Pipeline.StepFunction, + f10: Pipeline.StepFunction +): Pipeline.PipelineFunction; + +export function pipe< + ValueType, + ResultValue1, + ResultValue2, + ResultValue3, + ResultValue4, + ResultValue5, + ResultValue6, + ResultValue7, + ResultValue8, + ResultValue9, + ResultValue10, + ReturnType +>( + f1: Pipeline.StepFunction, + f2: Pipeline.StepFunction, + f3: Pipeline.StepFunction, + f4: Pipeline.StepFunction, + f5: Pipeline.StepFunction, + f6: Pipeline.StepFunction, + f7: Pipeline.StepFunction, + f8: Pipeline.StepFunction, + f9: Pipeline.StepFunction, + f10: Pipeline.StepFunction, + f11: Pipeline.StepFunction +): Pipeline.PipelineFunction; + +export function pipe< + ValueType, + ResultValue1, + ResultValue2, + ResultValue3, + ResultValue4, + ResultValue5, + ResultValue6, + ResultValue7, + ResultValue8, + ResultValue9, + ResultValue10, + ResultValue11, + ReturnType +>( + f1: Pipeline.StepFunction, + f2: Pipeline.StepFunction, + f3: Pipeline.StepFunction, + f4: Pipeline.StepFunction, + f5: Pipeline.StepFunction, + f6: Pipeline.StepFunction, + f7: Pipeline.StepFunction, + f8: Pipeline.StepFunction, + f9: Pipeline.StepFunction, + f10: Pipeline.StepFunction, + f11: Pipeline.StepFunction, + f12: Pipeline.StepFunction +): Pipeline.PipelineFunction; + +export function pipe< + ValueType, + ResultValue1, + ResultValue2, + ResultValue3, + ResultValue4, + ResultValue5, + ResultValue6, + ResultValue7, + ResultValue8, + ResultValue9, + ResultValue10, + ResultValue11, + ReturnType +>( + f1: Pipeline.StepFunction, + f2: Pipeline.StepFunction, + f3: Pipeline.StepFunction, + f4: Pipeline.StepFunction, + f5: Pipeline.StepFunction, + f6: Pipeline.StepFunction, + f7: Pipeline.StepFunction, + f8: Pipeline.StepFunction, + f9: Pipeline.StepFunction, + f10: Pipeline.StepFunction, + f11: Pipeline.StepFunction, + f12: Pipeline.StepFunction +): Pipeline.PipelineFunction; + +export function pipe< + ValueType, + ResultValue1, + ResultValue2, + ResultValue3, + ResultValue4, + ResultValue5, + ResultValue6, + ResultValue7, + ResultValue8, + ResultValue9, + ResultValue10, + ResultValue11, + ResultValue12, + ReturnType +>( + f1: Pipeline.StepFunction, + f2: Pipeline.StepFunction, + f3: Pipeline.StepFunction, + f4: Pipeline.StepFunction, + f5: Pipeline.StepFunction, + f6: Pipeline.StepFunction, + f7: Pipeline.StepFunction, + f8: Pipeline.StepFunction, + f9: Pipeline.StepFunction, + f10: Pipeline.StepFunction, + f11: Pipeline.StepFunction, + f12: Pipeline.StepFunction, + f13: Pipeline.StepFunction +): Pipeline.PipelineFunction; + +export function pipe< + ValueType, + ResultValue1, + ResultValue2, + ResultValue3, + ResultValue4, + ResultValue5, + ResultValue6, + ResultValue7, + ResultValue8, + ResultValue9, + ResultValue10, + ResultValue11, + ResultValue12, + ResultValue13, + ReturnType +>( + f1: Pipeline.StepFunction, + f2: Pipeline.StepFunction, + f3: Pipeline.StepFunction, + f4: Pipeline.StepFunction, + f5: Pipeline.StepFunction, + f6: Pipeline.StepFunction, + f7: Pipeline.StepFunction, + f8: Pipeline.StepFunction, + f9: Pipeline.StepFunction, + f10: Pipeline.StepFunction, + f11: Pipeline.StepFunction, + f12: Pipeline.StepFunction, + f13: Pipeline.StepFunction, + f14: Pipeline.StepFunction +): Pipeline.PipelineFunction; + +export function pipe< + ValueType, + ResultValue1, + ResultValue2, + ResultValue3, + ResultValue4, + ResultValue5, + ResultValue6, + ResultValue7, + ResultValue8, + ResultValue9, + ResultValue10, + ResultValue11, + ResultValue12, + ResultValue13, + ResultValue14, + ReturnType +>( + f1: Pipeline.StepFunction, + f2: Pipeline.StepFunction, + f3: Pipeline.StepFunction, + f4: Pipeline.StepFunction, + f5: Pipeline.StepFunction, + f6: Pipeline.StepFunction, + f7: Pipeline.StepFunction, + f8: Pipeline.StepFunction, + f9: Pipeline.StepFunction, + f10: Pipeline.StepFunction, + f11: Pipeline.StepFunction, + f12: Pipeline.StepFunction, + f13: Pipeline.StepFunction, + f14: Pipeline.StepFunction, + f15: Pipeline.StepFunction +): Pipeline.PipelineFunction; + +export function pipe< + ValueType, + ResultValue1, + ResultValue2, + ResultValue3, + ResultValue4, + ResultValue5, + ResultValue6, + ResultValue7, + ResultValue8, + ResultValue9, + ResultValue10, + ResultValue11, + ResultValue12, + ResultValue13, + ResultValue14, + ResultValue15, + ReturnType +>( + f1: Pipeline.StepFunction, + f2: Pipeline.StepFunction, + f3: Pipeline.StepFunction, + f4: Pipeline.StepFunction, + f5: Pipeline.StepFunction, + f6: Pipeline.StepFunction, + f7: Pipeline.StepFunction, + f8: Pipeline.StepFunction, + f9: Pipeline.StepFunction, + f10: Pipeline.StepFunction, + f11: Pipeline.StepFunction, + f12: Pipeline.StepFunction, + f13: Pipeline.StepFunction, + f14: Pipeline.StepFunction, + f15: Pipeline.StepFunction, + f16: Pipeline.StepFunction +): Pipeline.PipelineFunction; + /** * Create a pipeline of Pipeline.StepFunctions executing one after the other. * @export @@ -146,7 +400,7 @@ export function pipe< export function pipe( ...functions: Array> ): Pipeline.PipelineFunction { - if (functions.length === 0 || !functions.every((fn: Function) => typeof fn === 'function')) { + if (functions.length === 0 || !functions.every(fn => typeof fn === 'function')) { throw new Error('A Pipeline.StepFunction must be a function.'); } diff --git a/packages/bautajs-core/src/decorators/__tests__/as-promise.test.ts b/packages/bautajs-core/src/decorators/test/as-promise.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/as-promise.test.ts rename to packages/bautajs-core/src/decorators/test/as-promise.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/as-value.test.ts b/packages/bautajs-core/src/decorators/test/as-value.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/as-value.test.ts rename to packages/bautajs-core/src/decorators/test/as-value.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/catch-error.test.ts b/packages/bautajs-core/src/decorators/test/catch-error.test.ts similarity index 66% rename from packages/bautajs-core/src/decorators/__tests__/catch-error.test.ts rename to packages/bautajs-core/src/decorators/test/catch-error.test.ts index eb68e953..89ae78b5 100644 --- a/packages/bautajs-core/src/decorators/__tests__/catch-error.test.ts +++ b/packages/bautajs-core/src/decorators/test/catch-error.test.ts @@ -3,6 +3,16 @@ import { createContext } from '../../utils/create-context'; import { BautaJSInstance } from '../../types'; describe('catchError pipeline tests', () => { + test('should throw the error without any custom error handler', () => { + const expectedError = new Error('crashhh!!!'); + const ctx = createContext({}); + const pipeline = pipe(() => { + throw new Error('crashhh!!!'); + }); + + expect(() => pipeline(null, ctx, {} as BautaJSInstance)).toThrow(expectedError); + }); + test('should set the given error handler', () => { const errorHandler = () => { throw new Error('error'); @@ -35,7 +45,7 @@ describe('catchError pipeline tests', () => { expect(errorHandler.mock.calls).toHaveLength(1); }); - test('should catch the error for nested pipelines', () => { + test('should catch the error for nested pipelines using an errorHandler in the outer pipeline', () => { const expected = new Error('error'); const errorHandler = () => { throw expected; @@ -51,6 +61,44 @@ describe('catchError pipeline tests', () => { expect(() => pipeline(null, ctx, {} as BautaJSInstance)).toThrow(expected); }); + test('should catch the error through both inner and outer pipeline custom error handler', () => { + const expected = new Error('error'); + const errorHandler = () => { + throw expected; + }; + const innerErrorHandler = () => { + throw new Error('inner error'); + }; + + const ctx = createContext({}); + const pipelineNested = pipe(() => { + throw new Error('crashhh!!!'); + }).catchError(innerErrorHandler); + const pipeline = pipe(() => { + return 'ok'; + }, pipelineNested).catchError(errorHandler); + + expect(() => pipeline(null, ctx, {} as BautaJSInstance)).toThrow(expected); + }); + + test('should catch the error through the inner pipeline custom error handler', () => { + const expected = new Error('inner error'); + + const innerErrorHandler = () => { + throw new Error('inner error'); + }; + + const ctx = createContext({}); + const pipelineNested = pipe(() => { + throw new Error('crashhh!!!'); + }).catchError(innerErrorHandler); + const pipeline = pipe(pipelineNested, () => { + return 'ok'; + }); + + expect(() => pipeline(null, ctx, {} as BautaJSInstance)).toThrow(expected); + }); + test('should allow multiple catchError in different pipelines', async () => { const errorHandler1 = () => { return 'is OK'; diff --git a/packages/bautajs-core/src/decorators/__tests__/fixtures/test-api-definitions.json b/packages/bautajs-core/src/decorators/test/fixtures/test-api-definitions.json similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/fixtures/test-api-definitions.json rename to packages/bautajs-core/src/decorators/test/fixtures/test-api-definitions.json diff --git a/packages/bautajs-core/src/decorators/__tests__/iif.test.ts b/packages/bautajs-core/src/decorators/test/iif.test.ts similarity index 76% rename from packages/bautajs-core/src/decorators/__tests__/iif.test.ts rename to packages/bautajs-core/src/decorators/test/iif.test.ts index ad302e71..c177264c 100644 --- a/packages/bautajs-core/src/decorators/__tests__/iif.test.ts +++ b/packages/bautajs-core/src/decorators/test/iif.test.ts @@ -44,4 +44,18 @@ describe('iif decorator', () => { 'Plastic is not fantastic!' ); }); + + test('should work even if the result of the if is a promise', async () => { + const saveEarthPipeline = pipe(() => 'Plastic is not fantastic!'); + const plasticLoversPipeline = pipe(() => 'Plastic is fantastic!'); + const myBigIf = pipe(async (prev: any) => + Promise.resolve(prev.includes('Plastic is fantastic!')) + ); + + const pipeline = pipe(saveEarthPipeline, iif(myBigIf, plasticLoversPipeline)); + + expect(pipeline({}, createContext({}), {} as BautaJSInstance)).toBe( + 'Plastic is not fantastic!' + ); + }); }); diff --git a/packages/bautajs-core/src/decorators/__tests__/map.test.ts b/packages/bautajs-core/src/decorators/test/map.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/map.test.ts rename to packages/bautajs-core/src/decorators/test/map.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/match.test.ts b/packages/bautajs-core/src/decorators/test/match.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/match.test.ts rename to packages/bautajs-core/src/decorators/test/match.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/pairwise.test.ts b/packages/bautajs-core/src/decorators/test/pairwise.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/pairwise.test.ts rename to packages/bautajs-core/src/decorators/test/pairwise.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/parallel-all-settled.test.ts b/packages/bautajs-core/src/decorators/test/parallel-all-settled.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/parallel-all-settled.test.ts rename to packages/bautajs-core/src/decorators/test/parallel-all-settled.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/parallel-map.test.ts b/packages/bautajs-core/src/decorators/test/parallel-map.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/parallel-map.test.ts rename to packages/bautajs-core/src/decorators/test/parallel-map.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/parallel.test.ts b/packages/bautajs-core/src/decorators/test/parallel.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/parallel.test.ts rename to packages/bautajs-core/src/decorators/test/parallel.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/pipeline.test.ts b/packages/bautajs-core/src/decorators/test/pipeline.test.ts similarity index 79% rename from packages/bautajs-core/src/decorators/__tests__/pipeline.test.ts rename to packages/bautajs-core/src/decorators/test/pipeline.test.ts index 4b4aecc9..01998995 100644 --- a/packages/bautajs-core/src/decorators/__tests__/pipeline.test.ts +++ b/packages/bautajs-core/src/decorators/test/pipeline.test.ts @@ -39,6 +39,34 @@ describe('pipe tests', () => { expect(pipeline(null, ctx, {} as BautaJSInstance)).toStrictEqual(expected); }); + test('should allow for up to 16 steps in a pipeline', async () => { + const expected = "Let's sing: LA LA LA LA LA LA LA LA LA LA LA LA LA LA LA "; + const firstValue = 'LA '; + const myStep = (prev: string) => `${prev}${firstValue}`; + + const pipeline = pipe( + () => "Let's sing: ", + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep, + myStep + ); + const ctx = createContext({}); + + expect(pipeline(null, ctx, {} as BautaJSInstance)).toStrictEqual(expected); + }); + test('should allow pipelines on pipe method', async () => { const expected = 'this will be showed'; const pipeline1 = pipe(() => expected); @@ -189,4 +217,34 @@ describe('pipe tests', () => { expect(onCanceled2).toHaveBeenCalledTimes(1); expect(onCanceled1).toHaveBeenCalledTimes(1); }); + + test('should only execute the onCanceled of the executed steps even with promises', async () => { + const onCanceled1 = jest.fn(); + const onCanceled2 = jest.fn(); + const onCanceled3 = jest.fn(); + const pipeline = pipe( + async (_, ctx) => { + ctx.token.onCancel(onCanceled1); + return Promise.resolve('first pipeline'); + }, + async (_, ctx) => { + ctx.token.onCancel(onCanceled2); + ctx.token.cancel(); + return Promise.resolve('second pipeline'); + }, + async (_, ctx) => { + ctx.token.onCancel(onCanceled3); + return Promise.resolve('third pipeline'); + } + ); + const ctx = createContext({}); + + await expect(() => pipeline(null, ctx, {} as BautaJSInstance)).rejects.toThrow( + new Error('Pipeline canceled due to a request cancellation.') + ); + + expect(onCanceled3).toHaveBeenCalledTimes(0); + expect(onCanceled2).toHaveBeenCalledTimes(1); + expect(onCanceled1).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/bautajs-core/src/decorators/__tests__/retry-when.test.ts b/packages/bautajs-core/src/decorators/test/retry-when.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/retry-when.test.ts rename to packages/bautajs-core/src/decorators/test/retry-when.test.ts diff --git a/packages/bautajs-core/src/decorators/__tests__/tap.test.ts b/packages/bautajs-core/src/decorators/test/tap.test.ts similarity index 100% rename from packages/bautajs-core/src/decorators/__tests__/tap.test.ts rename to packages/bautajs-core/src/decorators/test/tap.test.ts diff --git a/packages/bautajs-core/src/index.ts b/packages/bautajs-core/src/index.ts index d421e763..f13ab1d6 100644 --- a/packages/bautajs-core/src/index.ts +++ b/packages/bautajs-core/src/index.ts @@ -2,6 +2,7 @@ import { prepareToLog } from './utils/prepare-to-log'; export * from './bauta'; export * from './decorators/match'; +export * from './decorators/cache'; export * from './decorators/resolver'; export * from './decorators/step'; export * from './decorators/pipeline'; diff --git a/packages/bautajs-core/src/types.ts b/packages/bautajs-core/src/types.ts index ceb73046..c63cce78 100644 --- a/packages/bautajs-core/src/types.ts +++ b/packages/bautajs-core/src/types.ts @@ -82,7 +82,7 @@ export interface Dictionary { } // OpenAPI document -export interface OpenAPIV2Document extends OpenAPIV2.Document {} +export type OpenAPIV2Document = OpenAPIV2.Document; export interface OpenAPIV3Document extends OpenAPIV3.Document { basePath?: string; } @@ -127,17 +127,17 @@ export interface IValidationError extends Error { } export interface Logger { fatal(msg: string, ...args: any[]): void; - fatal(obj: {}, msg?: string, ...args: any[]): void; + fatal(obj: object, msg?: string, ...args: any[]): void; error(msg: string, ...args: any[]): void; - error(obj: {}, msg?: string, ...args: any[]): void; + error(obj: object, msg?: string, ...args: any[]): void; warn(msg: string, ...args: any[]): void; - warn(obj: {}, msg?: string, ...args: any[]): void; + warn(obj: object, msg?: string, ...args: any[]): void; info(msg: string, ...args: any[]): void; - info(obj: {}, msg?: string, ...args: any[]): void; + info(obj: object, msg?: string, ...args: any[]): void; debug(msg: string, ...args: any[]): void; - debug(obj: {}, msg?: string, ...args: any[]): void; + debug(obj: object, msg?: string, ...args: any[]): void; trace(msg: string, ...args: any[]): void; - trace(obj: {}, msg?: string, ...args: any[]): void; + trace(obj: object, msg?: string, ...args: any[]): void; child: (bindings: Bindings) => Logger; level?: string | number | (() => number | string); } @@ -443,6 +443,7 @@ export interface Session { url?: string; } +// eslint-disable-next-line @typescript-eslint/no-namespace export declare namespace Pipeline { interface CatchError { (error: GenericError, ctx: Context, batuajs: BautaJSInstance): ErrorType; @@ -479,4 +480,4 @@ export interface CancelableToken { onCancel: (fn: OnCancel) => void; } -export interface CancelablePromise extends PCancelable {} +export type CancelablePromise = PCancelable; diff --git a/packages/bautajs-core/src/__tests__/bauta.logger.test.ts b/packages/bautajs-core/test/bauta.logger.test.ts similarity index 92% rename from packages/bautajs-core/src/__tests__/bauta.logger.test.ts rename to packages/bautajs-core/test/bauta.logger.test.ts index 86b9a00d..77d0fc9f 100644 --- a/packages/bautajs-core/src/__tests__/bauta.logger.test.ts +++ b/packages/bautajs-core/test/bauta.logger.test.ts @@ -1,7 +1,8 @@ +/* eslint-disable @typescript-eslint/ban-types */ import pino from 'pino'; -import { BautaJS } from '../index'; -import { Logger } from '../types'; -import { defaultLogger } from '../default-logger'; +import { BautaJS } from '../src/index'; +import { Logger } from '../src/types'; +import { defaultLogger } from '../src/default-logger'; const validLevels = ['trace', 'info', 'error', 'debug', 'fatal', 'warn']; const message = 'This is a general message'; @@ -31,6 +32,7 @@ describe('core tests', () => { test('should not initialize the core if using an invalid logger', async () => { const invalidLogger = defaultLogger(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore delete invalidLogger.info; // A custom made logger with no info is considered invalid diff --git a/packages/bautajs-core/src/__tests__/bauta.test.ts b/packages/bautajs-core/test/bauta.test.ts similarity index 99% rename from packages/bautajs-core/src/__tests__/bauta.test.ts rename to packages/bautajs-core/test/bauta.test.ts index ff50cd29..b78a97ed 100644 --- a/packages/bautajs-core/src/__tests__/bauta.test.ts +++ b/packages/bautajs-core/test/bauta.test.ts @@ -1,6 +1,6 @@ import fastSafeStringify from 'fast-safe-stringify'; import path from 'path'; -import { BautaJS, pipe, resolver, OnCancel, Document, CancelablePromise } from '../index'; +import { BautaJS, pipe, resolver, OnCancel, Document, CancelablePromise } from '../src/index'; import testApiDefinitionsJson from './fixtures/test-api-definitions.json'; import testApiDefinitions2VersionsJson from './fixtures/test-api-definition-2-versions.json'; @@ -179,7 +179,6 @@ describe('bauta core tests', () => { try { await bautaJS.operations.operation1.run({ req, res }); } catch (e: any) { - // eslint-disable-next-line jest/no-conditional-expect expect(e.stack).toBe(`${e.name}: ${e.message} \n ${fastSafeStringify(e, undefined, 2)}`); } }); @@ -527,10 +526,8 @@ describe('bauta core tests', () => { request1.cancel(); expect.assertions(3); - // eslint-disable-next-line jest/valid-expect-in-promise const [, req2] = await Promise.all([ request1.catch((e: any) => { - // eslint-disable-next-line jest/no-conditional-expect expect(e).toStrictEqual(expect.objectContaining({ message: 'Promise was canceled' })); return Promise.resolve({}); }), diff --git a/packages/bautajs-decorator-cache/src/__tests__/cache-setup.test.ts b/packages/bautajs-core/test/cache-setup.test.ts similarity index 84% rename from packages/bautajs-decorator-cache/src/__tests__/cache-setup.test.ts rename to packages/bautajs-core/test/cache-setup.test.ts index 13c813ba..88fa9566 100644 --- a/packages/bautajs-decorator-cache/src/__tests__/cache-setup.test.ts +++ b/packages/bautajs-core/test/cache-setup.test.ts @@ -1,5 +1,7 @@ -import { pipe, BautaJSInstance, createContext } from '@axa/bautajs-core'; -import { cache } from '../index'; +import { createContext } from '../src/utils/create-context'; +import { pipe } from '../src/index'; +import { cache } from '../src/decorators/cache'; +import { BautaJSInstance } from '../src/types'; import { sleep } from './utils'; describe('cache setup', () => { diff --git a/packages/bautajs-decorator-cache/src/__tests__/cache-usage.test.ts b/packages/bautajs-core/test/cache-usage.test.ts similarity index 95% rename from packages/bautajs-decorator-cache/src/__tests__/cache-usage.test.ts rename to packages/bautajs-core/test/cache-usage.test.ts index dc472e66..2c781d13 100644 --- a/packages/bautajs-decorator-cache/src/__tests__/cache-usage.test.ts +++ b/packages/bautajs-core/test/cache-usage.test.ts @@ -1,16 +1,12 @@ -import { pipe, BautaJSInstance, createContext } from '@axa/bautajs-core'; -import { cache, CacheStepFunction } from '../index'; -import { Normalizer } from '../types'; +import { pipe } from '../src/index'; +import { createContext } from '../src/utils/create-context'; +import { cache, CacheStepFunction, Normalizer } from '../src/decorators/cache'; +import { BautaJSInstance } from '../src/types'; import { sleep } from './utils'; describe('cache decorator usage', () => { let myCachePipeline: CacheStepFunction; - beforeAll(() => { - process.env.LOG_LEVEL = 'debug'; - process.env.DEBUG = 'bautajs*'; - }); - describe('normalizer using context req param', () => { beforeEach(async () => { const normalizer: Normalizer = (_, ctx) => ctx.data.value; diff --git a/packages/bautajs-core/src/__tests__/create-context.test.ts b/packages/bautajs-core/test/create-context.test.ts similarity index 94% rename from packages/bautajs-core/src/__tests__/create-context.test.ts rename to packages/bautajs-core/test/create-context.test.ts index 2d503a11..4748190f 100644 --- a/packages/bautajs-core/src/__tests__/create-context.test.ts +++ b/packages/bautajs-core/test/create-context.test.ts @@ -1,5 +1,5 @@ -import { defaultLogger } from '../default-logger'; -import { createContext } from '../utils/create-context'; +import { defaultLogger } from '../src/default-logger'; +import { createContext } from '../src/utils/create-context'; describe('create context tests', () => { test('should generate a request id', () => { diff --git a/packages/bautajs-core/src/__tests__/fixtures/api-definition-not-used-schema-swagger2.json b/packages/bautajs-core/test/fixtures/api-definition-not-used-schema-swagger2.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/api-definition-not-used-schema-swagger2.json rename to packages/bautajs-core/test/fixtures/api-definition-not-used-schema-swagger2.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/api-definition-not-used-schema.json b/packages/bautajs-core/test/fixtures/api-definition-not-used-schema.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/api-definition-not-used-schema.json rename to packages/bautajs-core/test/fixtures/api-definition-not-used-schema.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/api-definition-with-null.json b/packages/bautajs-core/test/fixtures/api-definition-with-null.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/api-definition-with-null.json rename to packages/bautajs-core/test/fixtures/api-definition-with-null.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/circular-schema.json b/packages/bautajs-core/test/fixtures/circular-schema.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/circular-schema.json rename to packages/bautajs-core/test/fixtures/circular-schema.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/nullable-schema.json b/packages/bautajs-core/test/fixtures/nullable-schema.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/nullable-schema.json rename to packages/bautajs-core/test/fixtures/nullable-schema.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/schema-two-operations.json b/packages/bautajs-core/test/fixtures/schema-two-operations.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/schema-two-operations.json rename to packages/bautajs-core/test/fixtures/schema-two-operations.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/schema-with-200-response-code.json b/packages/bautajs-core/test/fixtures/schema-with-200-response-code.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/schema-with-200-response-code.json rename to packages/bautajs-core/test/fixtures/schema-with-200-response-code.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/schema-with-400-response-code.json b/packages/bautajs-core/test/fixtures/schema-with-400-response-code.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/schema-with-400-response-code.json rename to packages/bautajs-core/test/fixtures/schema-with-400-response-code.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/schema-with-custom-format.json b/packages/bautajs-core/test/fixtures/schema-with-custom-format.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/schema-with-custom-format.json rename to packages/bautajs-core/test/fixtures/schema-with-custom-format.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/schema-with-format.json b/packages/bautajs-core/test/fixtures/schema-with-format.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/schema-with-format.json rename to packages/bautajs-core/test/fixtures/schema-with-format.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/schema-without-default-response.json b/packages/bautajs-core/test/fixtures/schema-without-default-response.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/schema-without-default-response.json rename to packages/bautajs-core/test/fixtures/schema-without-default-response.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/small-object-less-than-3200-size.json b/packages/bautajs-core/test/fixtures/small-object-less-than-3200-size.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/small-object-less-than-3200-size.json rename to packages/bautajs-core/test/fixtures/small-object-less-than-3200-size.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-api-definition-2-versions.json b/packages/bautajs-core/test/fixtures/test-api-definition-2-versions.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-api-definition-2-versions.json rename to packages/bautajs-core/test/fixtures/test-api-definition-2-versions.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-api-definitions-no-array.json b/packages/bautajs-core/test/fixtures/test-api-definitions-no-array.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-api-definitions-no-array.json rename to packages/bautajs-core/test/fixtures/test-api-definitions-no-array.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-api-definitions.json b/packages/bautajs-core/test/fixtures/test-api-definitions.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-api-definitions.json rename to packages/bautajs-core/test/fixtures/test-api-definitions.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-long-string-stream.json b/packages/bautajs-core/test/fixtures/test-long-string-stream.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-long-string-stream.json rename to packages/bautajs-core/test/fixtures/test-long-string-stream.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-path-schema.json b/packages/bautajs-core/test/fixtures/test-path-schema.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-path-schema.json rename to packages/bautajs-core/test/fixtures/test-path-schema.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-resolvers/operation-resolver-1.js b/packages/bautajs-core/test/fixtures/test-resolvers/operation-resolver-1.js similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-resolvers/operation-resolver-1.js rename to packages/bautajs-core/test/fixtures/test-resolvers/operation-resolver-1.js diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-resolvers/operation-resolver.js b/packages/bautajs-core/test/fixtures/test-resolvers/operation-resolver.js similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-resolvers/operation-resolver.js rename to packages/bautajs-core/test/fixtures/test-resolvers/operation-resolver.js diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-resolvers/service-providers.json b/packages/bautajs-core/test/fixtures/test-resolvers/service-providers.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-resolvers/service-providers.json rename to packages/bautajs-core/test/fixtures/test-resolvers/service-providers.json diff --git a/packages/bautajs-core/src/__tests__/fixtures/test-schema-rare-cases.json b/packages/bautajs-core/test/fixtures/test-schema-rare-cases.json similarity index 100% rename from packages/bautajs-core/src/__tests__/fixtures/test-schema-rare-cases.json rename to packages/bautajs-core/test/fixtures/test-schema-rare-cases.json diff --git a/packages/bautajs-core/src/__tests__/operation.test.ts b/packages/bautajs-core/test/operation.test.ts similarity index 97% rename from packages/bautajs-core/src/__tests__/operation.test.ts rename to packages/bautajs-core/test/operation.test.ts index d3a08b21..6b44f7b0 100644 --- a/packages/bautajs-core/src/__tests__/operation.test.ts +++ b/packages/bautajs-core/test/operation.test.ts @@ -2,13 +2,13 @@ import { Readable, Writable } from 'stream'; import httpMocks from 'node-mocks-http'; import { EventEmitter } from 'events'; import { ObjectWritableMock } from 'stream-mock'; -import { OperationBuilder } from '../core/operation'; -import { Operation, Route, BautaJSInstance, OpenAPIV3Document, RawContext } from '../types'; +import { OperationBuilder } from '../src/core/operation'; +import { Operation, Route, BautaJSInstance, OpenAPIV3Document, RawContext } from '../src/types'; import testApiDefinitionsJson from './fixtures/test-api-definitions.json'; import testSchemaRareCasesJson from './fixtures/test-schema-rare-cases.json'; -import { pipe, BautaJS } from '../index'; -import Parser from '../open-api/parser'; -import { asPromise } from '../decorators/as-promise'; +import { pipe, BautaJS } from '../src/index'; +import Parser from '../src/open-api/parser'; +import { asPromise } from '../src/decorators/as-promise'; describe('operation class tests', () => { let route: Route; @@ -416,12 +416,11 @@ describe('operation class tests', () => { expect(result).toBeNull(); }); - // eslint-disable-next-line jest/no-done-callback test('should not validate the response if the response is a stream', done => { const { input: inputStreamTest, expected: expectedStream - // eslint-disable-next-line global-require + // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires } = require('./fixtures/test-long-string-stream.json'); streamOperationTest.setup( diff --git a/packages/bautajs-core/src/__tests__/prepare-to-log.test.ts b/packages/bautajs-core/test/prepare-to-log.test.ts similarity index 97% rename from packages/bautajs-core/src/__tests__/prepare-to-log.test.ts rename to packages/bautajs-core/test/prepare-to-log.test.ts index 736e2e74..d6f5d213 100644 --- a/packages/bautajs-core/src/__tests__/prepare-to-log.test.ts +++ b/packages/bautajs-core/test/prepare-to-log.test.ts @@ -1,4 +1,4 @@ -import { prepareToLog } from '../utils/prepare-to-log'; +import { prepareToLog } from '../src/utils/prepare-to-log'; import smallObject from './fixtures/small-object-less-than-3200-size.json'; diff --git a/packages/bautajs-decorator-cache/src/__tests__/utils.ts b/packages/bautajs-core/test/utils.ts similarity index 100% rename from packages/bautajs-decorator-cache/src/__tests__/utils.ts rename to packages/bautajs-core/test/utils.ts diff --git a/packages/bautajs-core/src/__tests__/validation.test.ts b/packages/bautajs-core/test/validation.test.ts similarity index 99% rename from packages/bautajs-core/src/__tests__/validation.test.ts rename to packages/bautajs-core/test/validation.test.ts index 1a9c9629..bc3c9b7a 100644 --- a/packages/bautajs-core/src/__tests__/validation.test.ts +++ b/packages/bautajs-core/test/validation.test.ts @@ -1,5 +1,5 @@ -import { BautaJS, resolver } from '../index'; -import { Document, RawContext } from '../types'; +import { BautaJS, resolver } from '../src/index'; +import { Document, RawContext } from '../src/types'; import circularSchema from './fixtures/circular-schema.json'; import formatSchema from './fixtures/schema-with-format.json'; import nullableSchema from './fixtures/nullable-schema.json'; diff --git a/packages/bautajs-core/tsconfig.json b/packages/bautajs-core/tsconfig.json index f091b46f..5f5870f6 100644 --- a/packages/bautajs-core/tsconfig.json +++ b/packages/bautajs-core/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base", + "extends": "@axa/bautajs-dev-config/tsconfig.base.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist", @@ -7,7 +7,6 @@ "es2020", ], }, - "include": ["src/**/*", "src/**/*.json"], "exclude": ["**/__tests__", "**/__mocks__"] } diff --git a/packages/bautajs-datasource-rest/jest.config.js b/packages/bautajs-datasource-rest/jest.config.js index 94db24d8..4216a4b1 100644 --- a/packages/bautajs-datasource-rest/jest.config.js +++ b/packages/bautajs-datasource-rest/jest.config.js @@ -1,3 +1,6 @@ -const config = require('../../jest.config.base'); +const config = require('@axa/bautajs-dev-config/jest.config.base'); -module.exports = { displayName: '@axa/bautajs-datasource-rest', ...config }; +module.exports = { + displayName: '@axa/bautajs-datasource-rest', + ...config +}; diff --git a/packages/bautajs-datasource-rest/package.json b/packages/bautajs-datasource-rest/package.json index 83bca3f0..483d7666 100644 --- a/packages/bautajs-datasource-rest/package.json +++ b/packages/bautajs-datasource-rest/package.json @@ -1,6 +1,6 @@ { "name": "@axa/bautajs-datasource-rest", - "version": "2.2.2", + "version": "3.0.0-alpha.2", "description": "A bautaJS datasource rest provider", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -9,6 +9,8 @@ ], "scripts": { "tsc": "tsc", + "build": "tsc --build", + "test": "jest", "example": "npx ts-node ./example/simple.ts" }, "repository": { @@ -26,11 +28,12 @@ ], "license": "SEE LICENSE IN LICENSE.txt", "engines": { - "node": ">=14", + "node": ">=16", "npm": ">=8" }, "dependencies": { - "@axa/bautajs-core": "^2.2.2", + "@axa/bautajs-core": "^3.0.0-alpha.2", + "@axa/bautajs-dev-config": "*", "@axa/native-proxy-agent": "^1.0.0", "@sindresorhus/is": "^4.6.0", "got": "^11.8.5" diff --git a/packages/bautajs-datasource-rest/src/serializers/err.ts b/packages/bautajs-datasource-rest/src/serializers/err.ts index 310e6f40..72b0690b 100644 --- a/packages/bautajs-datasource-rest/src/serializers/err.ts +++ b/packages/bautajs-datasource-rest/src/serializers/err.ts @@ -55,7 +55,7 @@ const outgoingErrProto = Object.create( export function errSerializer( error: RequestError, restProviderOptions: RestProviderOptions, - isDebugMode: boolean = false + isDebugMode = false ) { const err = Object.create(outgoingErrProto); err.responseTime = error.response?.timings?.phases.total; diff --git a/packages/bautajs-datasource-rest/src/serializers/req.ts b/packages/bautajs-datasource-rest/src/serializers/req.ts index 7c40fd8a..853f1738 100644 --- a/packages/bautajs-datasource-rest/src/serializers/req.ts +++ b/packages/bautajs-datasource-rest/src/serializers/req.ts @@ -49,7 +49,7 @@ function buildURL(url: URL) { export function reqSerializer( options: NormalizedOptions, restProviderOptions: RestProviderOptions, - isDebugMode: boolean = false + isDebugMode = false ) { const req = Object.create(outgoingReqProto); if (options.url) { diff --git a/packages/bautajs-datasource-rest/src/serializers/res.ts b/packages/bautajs-datasource-rest/src/serializers/res.ts index 114c2a8d..6e0937f1 100644 --- a/packages/bautajs-datasource-rest/src/serializers/res.ts +++ b/packages/bautajs-datasource-rest/src/serializers/res.ts @@ -40,7 +40,7 @@ const outgoingResProto = Object.create( export function resSerializer( response: GOTResponse, restProviderOptions: RestProviderOptions, - isDebugMode: boolean = false + isDebugMode = false ) { const res = Object.create(outgoingResProto); res.responseTime = response.timings?.phases.total; diff --git a/packages/bautajs-datasource-rest/src/__tests__/datasource-rest.test.ts b/packages/bautajs-datasource-rest/test/datasource-rest.test.ts similarity index 95% rename from packages/bautajs-datasource-rest/src/__tests__/datasource-rest.test.ts rename to packages/bautajs-datasource-rest/test/datasource-rest.test.ts index fefc3924..7418d518 100644 --- a/packages/bautajs-datasource-rest/src/__tests__/datasource-rest.test.ts +++ b/packages/bautajs-datasource-rest/test/datasource-rest.test.ts @@ -26,7 +26,7 @@ describe('provider rest', () => { describe('restProvider extend', () => { test('should allow to create your own rest provider', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); const Id = '123'; nock('https://google.com').get(`/${Id}`).reply(200, 'text'); @@ -54,7 +54,7 @@ describe('provider rest', () => { describe('got extends defaults', () => { test('should allow do a requests with GOT options and the built in agent', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); const Id = '123'; nock('https://google.com') @@ -81,7 +81,7 @@ describe('provider rest', () => { }); test('should add the response status code if an http error ocurres', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); const Id = '123'; nock('https://google.com').get(`/${Id}`).reply(404, { message: 'not found' }); @@ -107,7 +107,7 @@ describe('provider rest', () => { }); test('should add the response status code if an http error ocurres and message should be case insensitive', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); const Id = '123'; nock('https://google.com').get(`/${Id}`).reply(404, { Message: 'not found' }); @@ -133,7 +133,7 @@ describe('provider rest', () => { }); test('should allow do a requests with GOT options and the built in agent with de default set up', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); const Id = '123'; nock('https://google.com') .get(`/${Id}`) @@ -160,7 +160,7 @@ describe('provider rest', () => { describe('request cancellation', () => { test('should cancel the request if the a cancel is executed', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('http://pets.com').get('/v1/policies').reply(200, {}); const myContext = createContext({ req: {}, res: {}, log: bautajs.logger }); @@ -178,7 +178,7 @@ describe('provider rest', () => { }); test('should cancel the request if the a cancel is executed and the request is an stream', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('http://pets.com').get('/v1/policies').reply(200, {}); const myContext = createContext({ req: {}, res: {}, log: bautajs.logger }); @@ -204,7 +204,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com').post('/v1/policies', { test: '1234' }).reply(200, { bender: 'ok' }); @@ -252,7 +252,7 @@ describe('provider rest', () => { logger.level = 20; jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com').post('/v1/policies', { test: '1234' }).reply(200, { bender: 'ok' }); @@ -308,7 +308,7 @@ describe('provider rest', () => { return logger; } }; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com').post('/v1/policies', { test: '1234' }).reply(200, { bender: 'ok' }); @@ -356,7 +356,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com:3000') .post('/v1/policies', { test: '1234' }) @@ -407,7 +407,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); jest.spyOn(logger, 'info').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com') .post('/v1/policies', { @@ -460,7 +460,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com').post('/v1/policies', 'someString').reply(200, { bender: 'ok' }); @@ -524,7 +524,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'info').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com').post('/v1/policies', 'someString').reply(200, { bender: 'ok' }); @@ -591,7 +591,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com') .post('/v1/policies', 'someString') @@ -669,7 +669,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com') .post('/v1/policies', 'someString') @@ -744,7 +744,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com') .post('/v1/policies', 'someString') @@ -817,7 +817,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com').post('/v1/policies').reply(200, { bender: 'ok' }); @@ -885,7 +885,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com').post('/v1/policies').reply(200, { bender: 'ok' }); @@ -949,7 +949,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); const logResponseHook = () => { return (response: any) => { @@ -1001,7 +1001,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); const logErrorsHook = () => { return (error: RequestError) => { @@ -1048,7 +1048,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'debug').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com') .post('/v1/policies', "{ bender: 'ok', bender2: 'ok2', foo: 'boo' }") @@ -1121,7 +1121,7 @@ describe('provider rest', () => { jest.spyOn(logger, 'error').mockImplementation(); logger.child = () => logger; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com') .post('/v1/policies', "{ bender: 'ok', bender2: 'ok2', foo: 'boo' }") @@ -1178,7 +1178,7 @@ describe('provider rest', () => { }); test('should sent the query params to the provider', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); const Id = '123'; nock('https://google.com') @@ -1221,7 +1221,7 @@ describe('provider rest', () => { logger.child = () => { return logger; }; - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com/v1').get('/policies').replyWithError('something awful happened'); @@ -1267,7 +1267,7 @@ describe('provider rest', () => { logger.child = () => logger; jest.spyOn(logger, 'error').mockImplementation(); - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com/v1') .get('/policies') @@ -1319,7 +1319,7 @@ describe('provider rest', () => { logger.child = () => logger; jest.spyOn(logger, 'error').mockImplementation(); jest.spyOn(logger, 'debug').mockImplementation(); - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com/v1').get('/policies').reply(200, 'we force with this a parserError'); @@ -1384,7 +1384,7 @@ describe('provider rest', () => { }); test('should generate a meaningful error if there is an issue', async () => { - const { restProvider } = await import('../index'); + const { restProvider } = await import('../src/index'); nock('https://pets.com/v1').get('/policies').reply(200, '
', { 'content-type': 'text/html' diff --git a/packages/bautajs-datasource-rest/src/__tests__/fixtures/test-api-definition.json b/packages/bautajs-datasource-rest/test/fixtures/test-api-definition.json similarity index 100% rename from packages/bautajs-datasource-rest/src/__tests__/fixtures/test-api-definition.json rename to packages/bautajs-datasource-rest/test/fixtures/test-api-definition.json diff --git a/packages/bautajs-datasource-rest/tsconfig.json b/packages/bautajs-datasource-rest/tsconfig.json index 10fb42c3..5f9f2a19 100644 --- a/packages/bautajs-datasource-rest/tsconfig.json +++ b/packages/bautajs-datasource-rest/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base", + "extends": "@axa/bautajs-dev-config/tsconfig.base.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist" diff --git a/packages/bautajs-decorator-cache/.npmignore b/packages/bautajs-decorator-cache/.npmignore deleted file mode 100644 index 6dcf1e49..00000000 --- a/packages/bautajs-decorator-cache/.npmignore +++ /dev/null @@ -1,7 +0,0 @@ -docs/ -src/ -node_modules/ -documentation.md -jest.config.js -tsconfig.json -examples/ \ No newline at end of file diff --git a/packages/bautajs-decorator-cache/CHANGELOG.md b/packages/bautajs-decorator-cache/CHANGELOG.md deleted file mode 100644 index fabaad36..00000000 --- a/packages/bautajs-decorator-cache/CHANGELOG.md +++ /dev/null @@ -1,11 +0,0 @@ -# Change Log - -> :warning: This CHANGELOG.md file is decommissioned and not maintained anymore. For checking the release notes of Bauta.js, please see the [Bauta.js releases page](https://github.com/axa-group/bauta.js/releases). - -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -~~All notable changes to this project will be documented in this file.~~ - -#### 1.0.0 (2021-11-29) - -- Initial commit diff --git a/packages/bautajs-decorator-cache/LICENSE.TXT b/packages/bautajs-decorator-cache/LICENSE.TXT deleted file mode 100644 index 30a3c19f..00000000 --- a/packages/bautajs-decorator-cache/LICENSE.TXT +++ /dev/null @@ -1,25 +0,0 @@ -The MIT License (MIT) Copyright Š 2022 AXA Group - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -All AXA Companies constitute the "AXA Group" where "AXA Company" shall mean: - -(i) AXA, "SociÊtÊ Anonyme" with a Board of Directors (herein "AXA SA") having its principal offices at 25, avenue Matignon, 75008 Paris, registered on the Commercial Registry of Paris under the number 572 093 920; and - -(ii) any other company controlled by, or controlling AXA SA, with a company being considered as controlling another: - -when it holds directly or indirectly a portion of the capital according to it the majority of the voting rights in general meetings of shareholders of this company; -when it holds solely the majority of the voting rights in this company by virtue of an agreement concluded with other partners or shareholders and which is not contrary to the interest of the company; -when it determines de facto, by voting rights which it holds, the decisions in the general meetings of shareholders of this company; -in any event, when it holds, directly or indirectly, a portion of voting rights greater than 40% and when no other partner or shareholder holds directly or indirectly a portion which is greater than its own; and -(iii) any economic interest group or joint venture in which AXA SA and/or one or more other Companies of the AXA Group participates for at least 50% in operating costs; - -(iv) in the cases where the law applicable to a company limits voting rights or control (such as defined here in above), this company will be deemed to be a company of the AXA Group, if the voting rights in general shareholders' meetings or the control held by a Company of the AXA Group reaches the maximum amount fixed by said applicable law; and - -(v) any legal entity in which an AXA company holds a lower portion of the capital or of voting rights or cost participation than set forth above when such company is authorized to do business under the name "AXA" or under a name which include the "AXA" business name; - -(vi) and any other legal entity in which an AXA Company directly or indirectly has an economic interest. For the avoidance of doubt GIE AXA is deemed an AXA Company. diff --git a/packages/bautajs-decorator-cache/jest.config.js b/packages/bautajs-decorator-cache/jest.config.js deleted file mode 100644 index a05328e4..00000000 --- a/packages/bautajs-decorator-cache/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -const config = require('../../jest.config.base'); - -module.exports = { displayName: '@axa/bautajs-decorator-cache', ...config }; diff --git a/packages/bautajs-decorator-cache/package.json b/packages/bautajs-decorator-cache/package.json deleted file mode 100644 index 0f702a8b..00000000 --- a/packages/bautajs-decorator-cache/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@axa/bautajs-decorator-cache", - "version": "2.2.2", - "description": "A bautaJS cache decorator", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "files": [ - "dist/**/*" - ], - "scripts": { - "tsc": "tsc" - }, - "repository": { - "type": "git", - "url": "https://github.com/axa-group/bauta.js" - }, - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/" - }, - "keywords": [ - "bautajs", - "cache", - "middleware" - ], - "license": "SEE LICENSE IN LICENSE.txt", - "engines": { - "node": ">=14", - "npm": ">=8" - }, - "dependencies": { - "@axa/bautajs-core": "^2.2.2", - "node-object-hash": "^2.3.10", - "quick-lru-cjs": "^5.2.1" - } -} diff --git a/packages/bautajs-decorator-cache/src/__mocks__/memoizee.js b/packages/bautajs-decorator-cache/src/__mocks__/memoizee.js deleted file mode 100644 index 6ee58567..00000000 --- a/packages/bautajs-decorator-cache/src/__mocks__/memoizee.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = jest.fn(); diff --git a/packages/bautajs-decorator-cache/src/types.ts b/packages/bautajs-decorator-cache/src/types.ts deleted file mode 100644 index 8005936a..00000000 --- a/packages/bautajs-decorator-cache/src/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { BautaJSInstance, Context, Pipeline } from '@axa/bautajs-core'; -import QuickLRU from 'quick-lru'; - -export interface Normalizer { - (prev: TIn, ctx: Context, bautajs: BautaJSInstance): CacheKey; -} - -export interface CacheStepFunction extends Pipeline.StepFunction { - store: QuickLRU; -} diff --git a/packages/bautajs-decorator-cache/tsconfig.json b/packages/bautajs-decorator-cache/tsconfig.json deleted file mode 100644 index 10fb42c3..00000000 --- a/packages/bautajs-decorator-cache/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.base", - "compilerOptions": { - "rootDir": "./src", - "outDir": "./dist" - }, - "include": ["src/**/*", "src/**/*.json"], - "exclude": ["**/__tests__", "**/__mocks__"] -} diff --git a/jest.config.base.js b/packages/bautajs-dev-config/jest.config.base.js similarity index 68% rename from jest.config.base.js rename to packages/bautajs-dev-config/jest.config.base.js index b6950b58..32b13b8e 100644 --- a/jest.config.base.js +++ b/packages/bautajs-dev-config/jest.config.base.js @@ -2,18 +2,21 @@ module.exports = { testEnvironment: 'node', preset: 'ts-jest', testPathIgnorePatterns: ['/node_modules/', '/dist/', '/fixtures/'], - testMatch: ['**/?(*.)+(spec|test).ts?(x)'], + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], coverageReporters: ['lcov', 'text'], testResultsProcessor: 'jest-sonar-reporter', coverageDirectory: './coverage/', collectCoverageFrom: ['**/*.ts', '!**/node_modules/**', '!**/coverage/**', '!**/jest.config.js'], coveragePathIgnorePatterns: ['dist', 'benchmark'], collectCoverage: true, - globals: { - 'ts-jest': { - tsconfig: 'tsconfig.test.json', - diagnostics: false - } + transform: { + '/.[jt]sx?$/': [ + 'ts-jest', + { + tsconfig: '../bautajs-dev-config/tsconfig.test.json', + diagnostics: false + } + ] }, testTimeout: 30000 }; diff --git a/packages/bautajs-dev-config/package.json b/packages/bautajs-dev-config/package.json new file mode 100644 index 00000000..1e9dce7d --- /dev/null +++ b/packages/bautajs-dev-config/package.json @@ -0,0 +1,5 @@ +{ + "name": "@axa/bautajs-dev-config", + "version": "3.0.0-alpha.2", + "private": true +} diff --git a/tsconfig.base.json b/packages/bautajs-dev-config/tsconfig.base.json similarity index 76% rename from tsconfig.base.json rename to packages/bautajs-dev-config/tsconfig.base.json index 52cd8054..3461cf46 100644 --- a/tsconfig.base.json +++ b/packages/bautajs-dev-config/tsconfig.base.json @@ -1,4 +1,6 @@ { + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Default", "compilerOptions": { "resolveJsonModule": true, "alwaysStrict": true, @@ -17,9 +19,7 @@ "skipLibCheck": true, "sourceMap": true, "strict": true, - "target": "ES2019", - "paths": { - "*" : ["types/*"] - } - } + "target": "ES2019" + }, + "exclude": ["node_modules", "**/node_modules"] } \ No newline at end of file diff --git a/tsconfig.test.json b/packages/bautajs-dev-config/tsconfig.test.json similarity index 50% rename from tsconfig.test.json rename to packages/bautajs-dev-config/tsconfig.test.json index aba72d0a..27556ed7 100644 --- a/tsconfig.test.json +++ b/packages/bautajs-dev-config/tsconfig.test.json @@ -1,11 +1,10 @@ { - "extends": "./tsconfig.base.json", + "extends": "@axa/bautajs-dev-config/tsconfig.base.json", "compilerOptions": { "noEmit": true, "types": ["node", "jest"], "paths": { - "__mocks__/*" : ["__mocks__/*"], - "*" : ["types/*"] + "__mocks__/*" : ["__mocks__/*"] } } } \ No newline at end of file diff --git a/packages/bautajs-express-example/README.md b/packages/bautajs-express-example/README.md index c3a4ac28..5ca6bb1e 100644 --- a/packages/bautajs-express-example/README.md +++ b/packages/bautajs-express-example/README.md @@ -4,6 +4,13 @@ - This project example purpose is to showcase main features using simple examples. - This project example **does not** intend to show good practices using Node.js or security practices at all. Please be sure you follow security good practices on your Node.js API (i.e. adding [helmet](https://www.npmjs.com/package/helmet)). +## How to start + +- It is recommented that you are using node v18. +- `npm install` from the root project of the monorepo +- enter into `packages/bautajs-express-example` folder +- run npm script `npm run start` + ## List of exposed Services - GET `/api/articles` @@ -27,6 +34,11 @@ - Returns a string with a random fact from the input number - GET `api/factNumber2/{number}` - Returns an object with a random fact from the input number +- GET `api/multiple-path/{key}` +- GET `api/multiple-path/specific` + - These two endpoints help understand how the route ordering works +- GET `api/cancel/{number}` + - Returns a string if number is less than 10 seconds. Aborts after 10 seconds in the rest of the cases. ## Custom Logger example @@ -51,8 +63,7 @@ With this change it a custom logger, defined in custom-logger-bauta.js, is used ### Production - [@axa/bautajs-core@1.0.0](https://github.com/axa-group/bauta.js) - MIT* - - [@axa/bautajs-datasource-rest@1.0.0](https://github.com/axa-group/bauta.js) - MIT* - - [@axa/bautajs-decorator-cache@1.0.0](https://github.com/axa-group/bauta.js) - MIT* + - [@axa/bautajs-datasource-rest@1.0.0](https://github.com/axa-group/bauta.js) - MIT* - [@axa/bautajs-express@1.0.0](https://github.com/axa-group/bauta.js) - MIT* - [@hapi/boom@10.0.0](https://github.com/hapijs/boom) - BSD-3-Clause - [express@4.18.1](https://github.com/expressjs/express) - MIT diff --git a/packages/bautajs-express-example/api-definition.json b/packages/bautajs-express-example/api-definition.json index 7686a620..25cd4103 100644 --- a/packages/bautajs-express-example/api-definition.json +++ b/packages/bautajs-express-example/api-definition.json @@ -30,6 +30,38 @@ } } }, + "/cancel/{number}": { + "get": { + "summary": "service to test cancel request. Waits number seconds until an answer is given but cancel if number is greather than 10 seconds", + "operationId": "cancelRequest", + "parameters": [ + { + "name": "number", + "in": "path", + "description": "The number of seconds to wait", + "required": true, + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, "/randomYear": { "get": { "summary": "Provides information about a random year", @@ -179,27 +211,6 @@ } } }, - "/cats": { - "get": { - "summary": "List all fact cats", - "operationId": "cats", - "responses": { - "200": { - "description": "Something!" - }, - "default": { - "description": "unexpected error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, "/chuckfacts/{string}": { "get": { "summary": "get a list of facts related to Chuck Norris", @@ -320,6 +331,59 @@ } } } + }, + "/multiple-path/specific": { + "get": { + "summary": "Specific endpoint to test path collision and route ordering", + "operationId": "multiplePathSpecific", + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/multiple-path/{key}": { + "get": { + "summary": "General endpoint to test path collision and route ordering", + "operationId": "multiplePathGeneral", + "parameters": [ + { + "name": "key", + "in": "path", + "description": "The key of the minimap", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } } }, "components": { diff --git a/packages/bautajs-express-example/jest.config.js b/packages/bautajs-express-example/jest.config.js new file mode 100644 index 00000000..8a41b2fa --- /dev/null +++ b/packages/bautajs-express-example/jest.config.js @@ -0,0 +1,6 @@ +const config = require('@axa/bautajs-dev-config/jest.config.base'); + +module.exports = { + displayName: '@axa/bautajs-express-example', + ...config +}; diff --git a/packages/bautajs-express-example/package.json b/packages/bautajs-express-example/package.json index a1b0c3e9..9299391e 100644 --- a/packages/bautajs-express-example/package.json +++ b/packages/bautajs-express-example/package.json @@ -1,10 +1,11 @@ { "name": "@axa/bautajs-express-example", - "version": "2.2.2", + "version": "3.0.0-alpha.2", "description": "A bautaJS example in express", "main": "./server.js", "scripts": { - "start": "LOG_LEVEL=info DEBUG=bautajs* node ./server.js" + "start": "LOG_LEVEL=info DEBUG=bautajs* node ./server.js", + "test": "jest" }, "repository": { "type": "git", @@ -18,13 +19,12 @@ ], "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { - "@axa/bautajs-core": "^2.2.2", - "@axa/bautajs-datasource-rest": "^2.2.2", - "@axa/bautajs-decorator-cache": "^2.2.2", - "@axa/bautajs-express": "^2.2.2", - "@hapi/boom": "^10.0.0", - "express": "^4.18.1", - "pino": "^8.4.2", - "pino-pretty": "^9.1.0" + "@axa/bautajs-core": "^3.0.0-alpha.2", + "@axa/bautajs-datasource-rest": "^3.0.0-alpha.2", + "@axa/bautajs-express": "^3.0.0-alpha.2", + "@hapi/boom": "^10.0.1", + "express": "^4.18.2", + "pino": "^8.11.0", + "pino-pretty": "^10.0.0" } } diff --git a/packages/bautajs-express-example/server.js b/packages/bautajs-express-example/server.js index ff8a7b62..cefb94de 100644 --- a/packages/bautajs-express-example/server.js +++ b/packages/bautajs-express-example/server.js @@ -25,7 +25,7 @@ const bautaJS = require('./server/instances/bauta'); app.listen(8080, err => { if (err) throw err; - bautaJS.logger.info('Server listening on localhost: 3000'); + bautaJS.logger.info('Server listening on localhost: 8080'); }); })(); diff --git a/packages/bautajs-express-example/server/datasources/cats-datasource.js b/packages/bautajs-express-example/server/datasources/cats-datasource.js deleted file mode 100644 index 2888f749..00000000 --- a/packages/bautajs-express-example/server/datasources/cats-datasource.js +++ /dev/null @@ -1,12 +0,0 @@ -const { restProvider } = require('@axa/bautajs-datasource-rest'); - -// Used to test that an https works -const catsRestProviderWithHttps = restProvider(client => { - return client.get('https://cat-fact.herokuapp.com/facts', { - https: { rejectUnauthorized: false } - }); -}); - -module.exports = { - catsRestProviderWithHttps -}; diff --git a/packages/bautajs-express-example/server/resolvers/cache-resolver.js b/packages/bautajs-express-example/server/resolvers/cache-resolver.js index 57bd52d5..35a9bba4 100644 --- a/packages/bautajs-express-example/server/resolvers/cache-resolver.js +++ b/packages/bautajs-express-example/server/resolvers/cache-resolver.js @@ -1,6 +1,5 @@ const { getRequest } = require('@axa/bautajs-express'); -const { pipe, resolver } = require('@axa/bautajs-core'); -const { cache } = require('@axa/bautajs-decorator-cache'); +const { cache, pipe, resolver } = require('@axa/bautajs-core'); const { chuckProvider } = require('../datasources/chuck-datasource'); const normalizer = (_, ctx) => { diff --git a/packages/bautajs-express-example/server/resolvers/cancel-request-resolver.js b/packages/bautajs-express-example/server/resolvers/cancel-request-resolver.js new file mode 100644 index 00000000..925a7224 --- /dev/null +++ b/packages/bautajs-express-example/server/resolvers/cancel-request-resolver.js @@ -0,0 +1,44 @@ +const { resolver, pipe, step } = require('@axa/bautajs-core'); +const { getRequest } = require('@axa/bautajs-express'); + +const transformResponse = step(response => { + return { + message: response + }; +}); + +function getNumberFromRequestStep(_prev, ctx) { + const req = getRequest(ctx); + + const { number } = req.params; + + return number; +} + +function giveAnswerAfterWaitingWithTimeout() { + return step(async (number, ctx) => { + const timeout = number * 1000; + const promiseAnswer = new Promise(resolve => + setTimeout(() => { + resolve(`We have waited for ${number} seconds`); + }, timeout) + ); + + cancelTimeout = 3000; + const cancelator = new Promise(resolve => + setTimeout(() => { + ctx.token.cancel(); // If this triggers the promise does not resolve but it is cancelled + resolve('ended'); + }, cancelTimeout) + ); + + return Promise.race(await [promiseAnswer, cancelator]); + }); +} + +module.exports = resolver(operations => { + operations.cancelRequest + .validateRequest(false) + .validateResponse(false) + .setup(pipe(getNumberFromRequestStep, giveAnswerAfterWaitingWithTimeout(), transformResponse)); +}); diff --git a/packages/bautajs-express-example/server/resolvers/cats-resolver.js b/packages/bautajs-express-example/server/resolvers/cats-resolver.js deleted file mode 100644 index 8de9fd39..00000000 --- a/packages/bautajs-express-example/server/resolvers/cats-resolver.js +++ /dev/null @@ -1,6 +0,0 @@ -const { resolver } = require('@axa/bautajs-core'); -const { catsRestProviderWithHttps } = require('../datasources/cats-datasource'); - -module.exports = resolver(operations => { - operations.cats.validateRequest(false).validateResponse(false).setup(catsRestProviderWithHttps()); -}); diff --git a/packages/bautajs-express-example/server/resolvers/multiple-path-resolver.js b/packages/bautajs-express-example/server/resolvers/multiple-path-resolver.js new file mode 100644 index 00000000..db770e3c --- /dev/null +++ b/packages/bautajs-express-example/server/resolvers/multiple-path-resolver.js @@ -0,0 +1,32 @@ +const { pipe, step, resolver } = require('@axa/bautajs-core'); +const { getRequest } = require('@axa/bautajs-express'); + +const transformResponse = step(response => { + return { + message: response + }; +}); + +function generalStep(_prev, ctx) { + const req = getRequest(ctx); + + const { key } = req.params; + + return `This is the general text for requests and now we are receiving: ${key}`; +} + +function specificStep() { + return 'This is a simple text for requests to the specific path'; +} + +module.exports = resolver(operations => { + operations.multiplePathGeneral + .validateRequest(false) + .validateResponse(false) + .setup(pipe(generalStep, transformResponse)); + + operations.multiplePathSpecific + .validateRequest(false) + .validateResponse(false) + .setup(pipe(specificStep, transformResponse)); +}); diff --git a/packages/bautajs-express-example/test/express-example.test.js b/packages/bautajs-express-example/test/express-example.test.js new file mode 100644 index 00000000..98798706 --- /dev/null +++ b/packages/bautajs-express-example/test/express-example.test.js @@ -0,0 +1,150 @@ +const express = require('express'); +const nock = require('nock'); +const supertest = require('supertest'); + +const bautaJS = require('../server/instances/bauta'); + +describe('bautajs-express-example regressions tests', () => { + let app; + beforeAll(async () => { + nock.disableNetConnect(); + nock.enableNetConnect('127.0.0.1'); + app = express(); + + const router = await bautaJS.buildRouter(); + + app.use('/', router); + // Set default error handler + // eslint-disable-next-line @typescript-eslint/no-unused-vars + app.use((err, _req, res, _next) => { + res.json({ message: err.message, status: res.statusCode, errors: err.errors }).end(); + }); + }); + + afterAll(() => { + nock.cleanAll(); + nock.enableNetConnect(); + }); + + test('GET /api/articles should return a successful response', async () => { + nock('https://jsonplaceholder.typicode.com').get(`/posts`).reply(200); + + const res = await supertest(app).get('/api/articles'); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/chuckfacts/:string should return a successful response', async () => { + const stringParam = 'foo'; + nock('https://api.chucknorris.io').get(`/jokes/search?query=${stringParam}`).reply(200); + + const res = await supertest(app).get(`/api/chuckfacts/${stringParam}`); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/chuckfacts/:string should return a cached successful response', async () => { + const stringParam = 'bar'; + nock('https://api.chucknorris.io') + .get(`/jokes/search?query=${stringParam}`) + .reply(200, { fact: 'foo' }); + + const res = await supertest(app).get(`/api/chuckfacts/${stringParam}`); + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ fact: 'foo' }); + expect(nock.isDone()).toBe(true); + + const res2 = await supertest(app).get(`/api/chuckfacts/${stringParam}`); + expect(res2.statusCode).toBe(200); + expect(res.body).toEqual({ fact: 'foo' }); + }); + + test('GET api/cancel/:number should return a successful request for a number lower than 3', async () => { + const res = await supertest(app).get(`/api/cancel/2`); + expect(res.statusCode).toBe(200); + }); + + test('GET api/cancel/:number should return a server error response for a number higher than 3', async () => { + const res = await supertest(app).get(`/api/cancel/4`); + expect(res.statusCode).toBe(500); + }); + + test('minimap should create, get all and get by id successfully', async () => { + const resPost1 = await supertest(app).post('/api/minimap').send({ + key: 'farewell', + value: 'bye!' + }); + expect(resPost1.statusCode).toBe(200); + + const resGetById = await supertest(app).get(`/api/minimap/farewell`); + expect(resGetById.statusCode).toBe(200); + expect(resGetById.body).toEqual({ + farewell: 'bye!' + }); + + const resPost2 = await supertest(app).post('/api/minimap').send({ + key: 'hi', + value: 'hola!' + }); + expect(resPost2.statusCode).toBe(200); + + const resGetAll = await supertest(app).get(`/api/minimap`); + expect(resGetAll.statusCode).toBe(200); + expect(resGetAll.body).toEqual({ + farewell: 'bye!', + hi: 'hola!' + }); + }); + + test('GET /api/multiple-path/{key} should return a successful response', async () => { + const res = await supertest(app).get(`/api/multiple-path/hola`); + + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ + message: 'This is the general text for requests and now we are receiving: hola' + }); + }); + + test('GET /api/multiple-path/specific should return a successful response', async () => { + const res = await supertest(app).get(`/api/multiple-path/specific`); + + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ + message: 'This is a simple text for requests to the specific path' + }); + }); + + test('GET api/randomYear should return a successful response', async () => { + nock('http://numbersapi.com').get('/random/year?json').reply(200); + + const res = await supertest(app).get(`/api/randomYear`); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/randomYear should return a successful response', async () => { + nock('http://numbersapi.com').get('/random/year?json').reply(200); + + const res = await supertest(app).get(`/api/randomYear2`); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/factNumber/:number should return successful response', async () => { + const stringParam = '100'; + nock('http://numbersapi.com').get(`/${stringParam}/math`).reply(200); + + const res = await supertest(app).get(`/api/factNumber/${stringParam}`); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/factNumber2/:number should return successful response', async () => { + const stringParam = '100'; + nock('http://numbersapi.com').get(`/${stringParam}/math`).reply(200); + + const res = await supertest(app).get(`/api/factNumber2/${stringParam}`); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); +}); diff --git a/packages/bautajs-express/README.md b/packages/bautajs-express/README.md index 3b5ca8d4..3767dcd8 100644 --- a/packages/bautajs-express/README.md +++ b/packages/bautajs-express/README.md @@ -50,7 +50,6 @@ Licensed under the (MIT / Apache 2.0) License. - [helmet@5.1.0](https://github.com/helmetjs/helmet) - MIT - [openapi-types@12.0.0](https://github.com/kogosoftwarellc/open-api/tree/master/packages/openapi-types) - MIT - [pino@8.4.2](https://github.com/pinojs/pino) - MIT - - [route-order@0.1.0](https://github.com/sfrdmn/node-route-order) - MIT - [swagger-ui-express@4.4.0](https://github.com/scottie1984/swagger-ui-express) - MIT ### Development diff --git a/packages/bautajs-express/jest.config.js b/packages/bautajs-express/jest.config.js index 4b6ed9b4..3012482b 100644 --- a/packages/bautajs-express/jest.config.js +++ b/packages/bautajs-express/jest.config.js @@ -1,3 +1,6 @@ -const config = require('../../jest.config.base'); +const config = require('@axa/bautajs-dev-config/jest.config.base'); -module.exports = { displayName: '@axa/bautajs-express', ...config }; +module.exports = { + displayName: '@axa/bautajs-express', + ...config +}; diff --git a/packages/bautajs-express/package.json b/packages/bautajs-express/package.json index fc58338c..b42ea64a 100644 --- a/packages/bautajs-express/package.json +++ b/packages/bautajs-express/package.json @@ -1,6 +1,6 @@ { "name": "@axa/bautajs-express", - "version": "2.2.2", + "version": "3.0.0-alpha.2", "description": "A bautaJS Express.js plugin. Allow auto exposing the bautaJS operations.", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -8,7 +8,10 @@ "dist/**/*" ], "scripts": { - "tsc": "tsc" + "tsc": "tsc", + "build": "tsc --build", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", + "test": "jest" }, "repository": { "type": "git", @@ -25,19 +28,21 @@ ], "license": "SEE LICENSE IN LICENSE.txt", "engines": { - "node": ">=14", + "node": ">=16", "npm": ">=8" }, "dependencies": { - "@axa/bautajs-core": "^2.2.2", + "@axa/bautajs-core": "^3.0.0-alpha.2", "compression": "^1.7.4", "cors": "^2.8.5", - "express": "^4.18.1", + "express": "^4.18.2", "express-pino-logger": "^7.0.0", - "helmet": "^7.0.0", - "openapi-types": "^12.0.0", - "pino": "^8.4.2", - "route-order": "^0.1.0", - "swagger-ui-express": "^4.4.0" + "helmet": "^6.0.1", + "openapi-types": "^12.1.0", + "pino": "^8.11.0", + "swagger-ui-express": "^4.6.2" + }, + "devDependencies": { + "@axa/bautajs-dev-config": "*" } } diff --git a/packages/bautajs-express/src/index.ts b/packages/bautajs-express/src/index.ts index 94cdad13..bcc9ef72 100644 --- a/packages/bautajs-express/src/index.ts +++ b/packages/bautajs-express/src/index.ts @@ -1,8 +1,8 @@ import compression from 'compression'; import express, { Response, IRoute } from 'express'; -import routeOrder from 'route-order'; import * as bautajs from '@axa/bautajs-core'; import type { Logger as PinoLogger } from 'pino'; +import { sortRoutes } from './routes-order'; import { RouterOptions, ExpressRequest, @@ -50,11 +50,7 @@ export class BautaJSExpress extends bautajs.BautaJS { this.onResponseValidationError = options.onResponseValidationError; } - private addRoute( - operation: bautajs.Operation, - router: express.Router, - apiBasePath: string = '/api/' - ) { + private addRoute(operation: bautajs.Operation, router: express.Router, apiBasePath = '/api/') { const method = operation.route?.method.toLowerCase() as keyof Omit; const responses = operation.route?.schema.response; const { url = '' } = operation.route || {}; @@ -249,14 +245,10 @@ export class BautaJSExpress extends bautajs.BautaJS { const routes = this.processOperations(); - Object.keys(routes) - .sort(routeOrder()) - .forEach(route => { - const methods = Object.keys(routes[route]); - methods.forEach(method => - this.addRoute(routes[route][method], router, options.apiBasePath) - ); - }); + sortRoutes(Object.keys(routes)).forEach(route => { + const methods = Object.keys(routes[route]); + methods.forEach(method => this.addRoute(routes[route][method], router, options.apiBasePath)); + }); if (this.apiDefinition) { initExplorer( diff --git a/packages/bautajs-express/src/middlewares.ts b/packages/bautajs-express/src/middlewares.ts index ce243ed5..847223a4 100644 --- a/packages/bautajs-express/src/middlewares.ts +++ b/packages/bautajs-express/src/middlewares.ts @@ -72,8 +72,7 @@ export function initExpressPino( }; if (!opt || (opt && opt.enabled === true && !opt.options)) { const pino = expressPino({ - // @ts-ignore - logger, + logger: logger as any, genReqId: (req: any) => req.id, serializers: { req: reqSerializer, @@ -86,9 +85,8 @@ export function initExpressPino( router.use(pino); router.use(reqStartMw); } else if (opt && opt.enabled === true && opt.options) { - // @ts-ignore const pino = expressPino({ - logger, + logger: logger as any, genReqId: (req: any) => req.id, serializers: { req: reqSerializer, diff --git a/packages/bautajs-express/src/routes-order.ts b/packages/bautajs-express/src/routes-order.ts new file mode 100644 index 00000000..a15d4a0e --- /dev/null +++ b/packages/bautajs-express/src/routes-order.ts @@ -0,0 +1,33 @@ +const STATIC_ROUTE = 1; +const ROUTE_WITH_SUFFIX = 11; +const ROUTE_WITH_PARAM = 111; +const ROUTE_WITH_OPTIONAL_PARAM = 1111; + +const REGEXP_CONTAINS_OPTIONAL_PARAM = /^:(.*)\?/; +const REGEXP_CONTAINS_SUFFIX = /^:(.*)\./; +const REGEXP_CONTAINS_PARAM = /^:/; + +function toValue(str: string) { + if (str === '*') return Number.MAX_SAFE_INTEGER; + if (REGEXP_CONTAINS_OPTIONAL_PARAM.test(str)) return ROUTE_WITH_OPTIONAL_PARAM; + if (REGEXP_CONTAINS_SUFFIX.test(str)) return ROUTE_WITH_SUFFIX; + if (REGEXP_CONTAINS_PARAM.test(str)) return ROUTE_WITH_PARAM; + return STATIC_ROUTE; +} + +function toRank(str: string) { + const arr = str.split('/'); + const value = arr.reduce((acc, curr) => { + return acc + toValue(curr); + }, ''); + return (arr.length - 1) / +value; +} + +function compareRoutes(firstRoute: string, secondRoute: string) { + return toRank(secondRoute) - toRank(firstRoute); +} + +export function sortRoutes(routes: string[]) { + const workArray = [...routes]; + return workArray.sort(compareRoutes); +} diff --git a/packages/bautajs-express/src/types.ts b/packages/bautajs-express/src/types.ts index 61edb06e..a0eddaee 100644 --- a/packages/bautajs-express/src/types.ts +++ b/packages/bautajs-express/src/types.ts @@ -10,7 +10,6 @@ import { BautaJSOptions, ValidationError } from '@axa/bautajs-core'; -import P from 'pino'; export interface ICallback { (error?: GenericError, result?: any): void; @@ -78,9 +77,8 @@ export type OnResponseValidationError = ( res: express.Response ) => any; -// Since the request logger is express-pino, force the logger on options to be Pino export interface BautaJSExpressOptions extends BautaJSOptions { - logger?: P.Logger; + logger?: Logger; /** * Method executed after a response validation throws an error. * Since this happens after the error handler is executed you might need to reformat the validation errors. diff --git a/packages/bautajs-express/src/__tests__/express-validation.test.ts b/packages/bautajs-express/test/express-validation.test.ts similarity index 99% rename from packages/bautajs-express/src/__tests__/express-validation.test.ts rename to packages/bautajs-express/test/express-validation.test.ts index b8abfe45..8af11614 100644 --- a/packages/bautajs-express/src/__tests__/express-validation.test.ts +++ b/packages/bautajs-express/test/express-validation.test.ts @@ -2,7 +2,7 @@ import express from 'express'; import path from 'path'; import supertest from 'supertest'; -import { BautaJSExpress } from '../index'; +import { BautaJSExpress } from '../src/index'; const apiDefinitionsCustomValidation = require('./fixtures/test-api-definitions-custom-validation.json'); diff --git a/packages/bautajs-express/src/__tests__/express.test.ts b/packages/bautajs-express/test/express.test.ts similarity index 81% rename from packages/bautajs-express/src/__tests__/express.test.ts rename to packages/bautajs-express/test/express.test.ts index f45412ec..ebb71461 100644 --- a/packages/bautajs-express/src/__tests__/express.test.ts +++ b/packages/bautajs-express/test/express.test.ts @@ -5,13 +5,14 @@ import FormData from 'form-data'; import supertest from 'supertest'; import { Readable } from 'stream'; import { resolver, asPromise, defaultLogger } from '@axa/bautajs-core'; -import { BautaJSExpress } from '../index'; -import { getRequest, getResponse } from '../operators'; +import { BautaJSExpress } from '../src/index'; +import { getRequest, getResponse } from '../src/operators'; const apiDefinition = require('./fixtures/test-api-definitions.json'); const apiDefinitionV2 = require('./fixtures/test-api-definitions-v2.json'); const apiDefinitionSwagger2 = require('./fixtures/test-api-definitions-swagger-2.json'); const apiDefinitionSwaggerCircularDeps = require('./fixtures/test-api-definitions-swagger-circular-deps.json'); +const apiDefinitionSwaggerPathOrdering = require('./fixtures/test-api-definitions-swagger-path-ordering.json'); describe('bautaJS express', () => { describe('request cancellation', () => { @@ -44,7 +45,6 @@ describe('bautaJS express', () => { try { await request; } catch (e) { - // eslint-disable-next-line jest/no-conditional-expect expect(logger.error).toHaveBeenCalledWith( { message: 'Request was aborted by the requester intentionally' }, 'The request was canceled by the requester.' @@ -121,7 +121,6 @@ describe('bautaJS express', () => { expect(res.body).toStrictEqual({ ok: 'finished early' }); }); - // eslint-disable-next-line jest/expect-expect test('should not send the response again if already has been sent on a readable pipe', async () => { const bautajs = new BautaJSExpress({ apiDefinition, @@ -158,7 +157,6 @@ describe('bautaJS express', () => { .expect('123'); }); - // eslint-disable-next-line jest/expect-expect test('should not force empty object if the status code is 204', async () => { const bautajs = new BautaJSExpress({ apiDefinition, @@ -180,7 +178,6 @@ describe('bautaJS express', () => { await supertest(app).get('/v1/api/test').expect(204, ''); }); - // eslint-disable-next-line jest/expect-expect test('should not override the headers set on the pipeline by the swagger ones', async () => { const form = new FormData(); const bautajs = new BautaJSExpress({ @@ -337,4 +334,72 @@ describe('bautaJS express', () => { ]); }); }); + + describe('path ordering', () => { + test('should work with specific operation first in the resolver', async () => { + const bautajs = new BautaJSExpress({ + apiDefinition: apiDefinitionSwaggerPathOrdering, + resolversPath: path.resolve( + __dirname, + './fixtures/test-resolvers/path-ordering-specific-first-resolver.js' + ) + }); + + const router = await bautajs.buildRouter(); + + const app = express(); + app.use('/v1', router); + + const generalResponse = await supertest(app) + .get('/v1/api/multiple-path/cat') + .expect('Content-Type', /json/) + .expect(200); + + expect(generalResponse.body).toStrictEqual({ + message: 'This is the general text for requests and now we are receiving: cat' + }); + + const specificResponse = await supertest(app) + .get('/v1/api/multiple-path/specific') + .expect('Content-Type', /json/) + .expect(200); + + expect(specificResponse.body).toStrictEqual({ + message: 'This is a simple text for requests to the specific path' + }); + }); + + test('should work with general operation first in the resolver', async () => { + const bautajs = new BautaJSExpress({ + apiDefinition: apiDefinitionSwaggerPathOrdering, + resolversPath: path.resolve( + __dirname, + './fixtures/test-resolvers/path-ordering-general-first-resolver.js' + ) + }); + + const router = await bautajs.buildRouter(); + + const app = express(); + app.use('/v1', router); + + const generalResponse = await supertest(app) + .get('/v1/api/multiple-path/cat') + .expect('Content-Type', /json/) + .expect(200); + + expect(generalResponse.body).toStrictEqual({ + message: 'This is the general text for requests and now we are receiving: cat' + }); + + const specificResponse = await supertest(app) + .get('/v1/api/multiple-path/specific') + .expect('Content-Type', /json/) + .expect(200); + + expect(specificResponse.body).toStrictEqual({ + message: 'This is a simple text for requests to the specific path' + }); + }); + }); }); diff --git a/packages/bautajs-express/src/__tests__/fixtures/expected-api-full-unused-swagger.json b/packages/bautajs-express/test/fixtures/expected-api-full-unused-swagger.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/expected-api-full-unused-swagger.json rename to packages/bautajs-express/test/fixtures/expected-api-full-unused-swagger.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/expected-api-only-used-tags-swagger.json b/packages/bautajs-express/test/fixtures/expected-api-only-used-tags-swagger.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/expected-api-only-used-tags-swagger.json rename to packages/bautajs-express/test/fixtures/expected-api-only-used-tags-swagger.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/expected-api-unused-swagger.json b/packages/bautajs-express/test/fixtures/expected-api-unused-swagger.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/expected-api-unused-swagger.json rename to packages/bautajs-express/test/fixtures/expected-api-unused-swagger.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-custom-validation.json b/packages/bautajs-express/test/fixtures/test-api-definitions-custom-validation.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-custom-validation.json rename to packages/bautajs-express/test/fixtures/test-api-definitions-custom-validation.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-extra-tag.json b/packages/bautajs-express/test/fixtures/test-api-definitions-extra-tag.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-extra-tag.json rename to packages/bautajs-express/test/fixtures/test-api-definitions-extra-tag.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-swagger-2.json b/packages/bautajs-express/test/fixtures/test-api-definitions-swagger-2.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-swagger-2.json rename to packages/bautajs-express/test/fixtures/test-api-definitions-swagger-2.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-swagger-circular-deps.json b/packages/bautajs-express/test/fixtures/test-api-definitions-swagger-circular-deps.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-swagger-circular-deps.json rename to packages/bautajs-express/test/fixtures/test-api-definitions-swagger-circular-deps.json diff --git a/packages/bautajs-express/test/fixtures/test-api-definitions-swagger-path-ordering.json b/packages/bautajs-express/test/fixtures/test-api-definitions-swagger-path-ordering.json new file mode 100644 index 00000000..85f4eee1 --- /dev/null +++ b/packages/bautajs-express/test/fixtures/test-api-definitions-swagger-path-ordering.json @@ -0,0 +1,84 @@ + +{ + "openapi": "3.0.0", + "info": { + "version": "v1", + "title": "Swagger Petstore", + "license": { + "name": "MIT" + } + }, + "paths": { + "/multiple-path/specific": { + "get": { + "summary": "Specific endpoint to test path collision and route ordering", + "operationId": "multiplePathSpecific", + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/multiple-path/{key}": { + "get": { + "summary": "General endpoint to test path collision and route ordering", + "operationId": "multiplePathGeneral", + "parameters": [ + { + "name": "key", + "in": "path", + "description": "The key of the minimap", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Error": { + "required": [ + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-v2.json b/packages/bautajs-express/test/fixtures/test-api-definitions-v2.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-api-definitions-v2.json rename to packages/bautajs-express/test/fixtures/test-api-definitions-v2.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-api-definitions.json b/packages/bautajs-express/test/fixtures/test-api-definitions.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-api-definitions.json rename to packages/bautajs-express/test/fixtures/test-api-definitions.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-api-unused-definitions.json b/packages/bautajs-express/test/fixtures/test-api-unused-definitions.json similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-api-unused-definitions.json rename to packages/bautajs-express/test/fixtures/test-api-unused-definitions.json diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-resolvers/operation-resolver-error.js b/packages/bautajs-express/test/fixtures/test-resolvers/operation-resolver-error.js similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-resolvers/operation-resolver-error.js rename to packages/bautajs-express/test/fixtures/test-resolvers/operation-resolver-error.js diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-resolvers/operation-resolver-invalid.js b/packages/bautajs-express/test/fixtures/test-resolvers/operation-resolver-invalid.js similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-resolvers/operation-resolver-invalid.js rename to packages/bautajs-express/test/fixtures/test-resolvers/operation-resolver-invalid.js diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-resolvers/operation-resolver.js b/packages/bautajs-express/test/fixtures/test-resolvers/operation-resolver.js similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-resolvers/operation-resolver.js rename to packages/bautajs-express/test/fixtures/test-resolvers/operation-resolver.js diff --git a/packages/bautajs-express/test/fixtures/test-resolvers/path-ordering-general-first-resolver.js b/packages/bautajs-express/test/fixtures/test-resolvers/path-ordering-general-first-resolver.js new file mode 100644 index 00000000..368905be --- /dev/null +++ b/packages/bautajs-express/test/fixtures/test-resolvers/path-ordering-general-first-resolver.js @@ -0,0 +1,32 @@ +const { pipe, step, resolver } = require('@axa/bautajs-core'); +const { getRequest } = require('../../../src/index'); + +const transformResponse = step(response => { + return { + message: response + }; +}); + +function generalStep(_prev, ctx) { + const req = getRequest(ctx); + + const { key } = req.params; + + return `This is the general text for requests and now we are receiving: ${key}`; +} + +function specificStep() { + return 'This is a simple text for requests to the specific path'; +} + +module.exports = resolver(operations => { + operations.multiplePathGeneral + .validateRequest(false) + .validateResponse(false) + .setup(pipe(generalStep, transformResponse)); + + operations.multiplePathSpecific + .validateRequest(false) + .validateResponse(false) + .setup(pipe(specificStep, transformResponse)); +}); diff --git a/packages/bautajs-express/test/fixtures/test-resolvers/path-ordering-specific-first-resolver.js b/packages/bautajs-express/test/fixtures/test-resolvers/path-ordering-specific-first-resolver.js new file mode 100644 index 00000000..20fa97c9 --- /dev/null +++ b/packages/bautajs-express/test/fixtures/test-resolvers/path-ordering-specific-first-resolver.js @@ -0,0 +1,32 @@ +const { pipe, step, resolver } = require('@axa/bautajs-core'); +const { getRequest } = require('../../../src/index'); + +const transformResponse = step(response => { + return { + message: response + }; +}); + +function generalStep(_prev, ctx) { + const req = getRequest(ctx); + + const { key } = req.params; + + return `This is the general text for requests and now we are receiving: ${key}`; +} + +function specificStep() { + return 'This is a simple text for requests to the specific path'; +} + +module.exports = resolver(operations => { + operations.multiplePathSpecific + .validateRequest(false) + .validateResponse(false) + .setup(pipe(specificStep, transformResponse)); + + operations.multiplePathGeneral + .validateRequest(false) + .validateResponse(false) + .setup(pipe(generalStep, transformResponse)); +}); diff --git a/packages/bautajs-express/src/__tests__/fixtures/test-resolvers/private-operation-resolver.js b/packages/bautajs-express/test/fixtures/test-resolvers/private-operation-resolver.js similarity index 100% rename from packages/bautajs-express/src/__tests__/fixtures/test-resolvers/private-operation-resolver.js rename to packages/bautajs-express/test/fixtures/test-resolvers/private-operation-resolver.js diff --git a/packages/bautajs-express/src/__tests__/middlewares.test.ts b/packages/bautajs-express/test/middlewares.test.ts similarity index 96% rename from packages/bautajs-express/src/__tests__/middlewares.test.ts rename to packages/bautajs-express/test/middlewares.test.ts index dafe67ff..ff2bec56 100644 --- a/packages/bautajs-express/src/__tests__/middlewares.test.ts +++ b/packages/bautajs-express/test/middlewares.test.ts @@ -3,9 +3,9 @@ import split from 'split2'; import pino from 'pino'; import supertest from 'supertest'; -import { resolver, defaultLogger } from '@axa/bautajs-core'; +import { resolver, defaultLogger, Logger } from '@axa/bautajs-core'; import express from 'express'; -import { BautaJSExpress } from '../index'; +import { BautaJSExpress } from '../src/index'; const apiDefinitionExtraTag = require('./fixtures/test-api-definitions-extra-tag.json'); const apiDefinition = require('./fixtures/test-api-unused-definitions.json'); @@ -108,10 +108,8 @@ describe('express middlewares', () => { describe('express pino logger', () => { test('pino logger can be disabled', async () => { - const logger = defaultLogger(); - logger.child = () => { - return logger; - }; + const logger: Logger = defaultLogger(); + logger.child = () => logger; const spy = jest.spyOn(logger, 'info'); const bautajs = new BautaJSExpress({ logger, @@ -149,10 +147,8 @@ describe('express middlewares', () => { }); test('incoming request must be logged with pino express', async () => { - const logger = defaultLogger(); - logger.child = () => { - return logger; - }; + const logger: Logger = defaultLogger(); + logger.child = () => logger; const spy = jest.spyOn(logger, 'info'); const bautajs = new BautaJSExpress({ logger, diff --git a/packages/bautajs-express/test/routes-order.test.ts b/packages/bautajs-express/test/routes-order.test.ts new file mode 100644 index 00000000..de80f17b --- /dev/null +++ b/packages/bautajs-express/test/routes-order.test.ts @@ -0,0 +1,71 @@ +import { sortRoutes } from '../src/routes-order'; + +describe('sortRoutes', () => { + test('should work with the order as expected without optional parameters', () => { + const input = [ + '*', + 'bautajs/specific', + 'bautajs/*', + 'bautajs/:key', + 'bautajs/inner/:key', + 'bautajs/inner/specific' + ]; + const result = sortRoutes(input); + + expect(result).toStrictEqual([ + 'bautajs/specific', + 'bautajs/inner/specific', + 'bautajs/:key', + 'bautajs/inner/:key', + 'bautajs/*', + '*' + ]); + }); + + test('should work with the order as expected with optional parameters', () => { + const input = [ + 'bautajs/specific', + 'bautajs/*', + 'bautajs/:key?', + 'bautajs/inner/:key?', + 'bautajs/inner/specific', + 'bautajs/inner/*', + 'bautajs' + ]; + const result = sortRoutes(input); + + expect(result).toStrictEqual([ + 'bautajs/specific', + 'bautajs/inner/specific', + 'bautajs/:key?', + 'bautajs/inner/:key?', + 'bautajs/*', + 'bautajs/inner/*', + 'bautajs' + ]); + }); + + test('should not modify input array', () => { + const input = [ + 'bautajs/specific', + 'bautajs/*', + 'bautajs/:key?', + 'bautajs/inner/:key?', + 'bautajs/inner/specific', + 'bautajs/inner/*', + 'bautajs' + ]; + + sortRoutes(input); + + expect(input).toStrictEqual([ + 'bautajs/specific', + 'bautajs/*', + 'bautajs/:key?', + 'bautajs/inner/:key?', + 'bautajs/inner/specific', + 'bautajs/inner/*', + 'bautajs' + ]); + }); +}); diff --git a/packages/bautajs-express/tsconfig.json b/packages/bautajs-express/tsconfig.json index 10fb42c3..5f9f2a19 100644 --- a/packages/bautajs-express/tsconfig.json +++ b/packages/bautajs-express/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base", + "extends": "@axa/bautajs-dev-config/tsconfig.base.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist" diff --git a/packages/bautajs-fastify-example/README.md b/packages/bautajs-fastify-example/README.md index d37a35df..cfce9026 100644 --- a/packages/bautajs-fastify-example/README.md +++ b/packages/bautajs-fastify-example/README.md @@ -4,6 +4,13 @@ - This project example purpose is to showcase main features using simple examples. - This project example **does not** intend to show good practices using Node.js or security practices at all. Please be sure you follow security good practices on your Node.js API (i.e. adding [helmet](@fastify/helmet)). +## How to start + +- It is recommented that you are using node v18. +- `npm install` from the root project of the monorepo +- enter into `packages/bautajs-fastify-example` folder +- run npm script `npm run start` + ## List of exposed Services - GET `/api/articles` @@ -11,8 +18,6 @@ - GET `/api/chuckfacts/{string}` - Returns a list of chuckfacts from the string - Shows how to use the cache decorator in a resolver -- GET `/api/cats` - - Returns a list of cat facts - GET `/api/minimap` - Returns an object with all the defined key-values - GET `/api/minimap/${key}` @@ -27,13 +32,17 @@ - Returns a string with a random fact from the input number - GET `api/factNumber2/{number}` - Returns an object with a random fact from the input number +- GET `api/multiple-path/{key}` +- GET `api/multiple-path/specific` + - These two endpoints help understand how the route ordering works +- GET `api/cancel/{number}` + - Returns a string if number is lower than 3 seconds. Aborts after 3 seconds in the rest of the cases. ## Third party dependencies licenses ### Production - [@axa/bautajs-core@2.0.0](https://github.com/axa-group/bauta.js) - MIT* - - [@axa/bautajs-datasource-rest@2.0.0](https://github.com/axa-group/bauta.js) - MIT* - - [@axa/bautajs-decorator-cache@2.0.0](https://github.com/axa-group/bauta.js) - MIT* + - [@axa/bautajs-datasource-rest@2.0.0](https://github.com/axa-group/bauta.js) - MIT* - [@axa/bautajs-fastify@2.0.0](https://github.com/axa-group/bauta.js) - MIT* - [fastify@4.8.1](https://github.com/fastify/fastify) - MIT diff --git a/packages/bautajs-fastify-example/api-definition.json b/packages/bautajs-fastify-example/api-definition.json index 7686a620..25cd4103 100644 --- a/packages/bautajs-fastify-example/api-definition.json +++ b/packages/bautajs-fastify-example/api-definition.json @@ -30,6 +30,38 @@ } } }, + "/cancel/{number}": { + "get": { + "summary": "service to test cancel request. Waits number seconds until an answer is given but cancel if number is greather than 10 seconds", + "operationId": "cancelRequest", + "parameters": [ + { + "name": "number", + "in": "path", + "description": "The number of seconds to wait", + "required": true, + "schema": { + "type": "number" + } + } + ], + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, "/randomYear": { "get": { "summary": "Provides information about a random year", @@ -179,27 +211,6 @@ } } }, - "/cats": { - "get": { - "summary": "List all fact cats", - "operationId": "cats", - "responses": { - "200": { - "description": "Something!" - }, - "default": { - "description": "unexpected error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, "/chuckfacts/{string}": { "get": { "summary": "get a list of facts related to Chuck Norris", @@ -320,6 +331,59 @@ } } } + }, + "/multiple-path/specific": { + "get": { + "summary": "Specific endpoint to test path collision and route ordering", + "operationId": "multiplePathSpecific", + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/multiple-path/{key}": { + "get": { + "summary": "General endpoint to test path collision and route ordering", + "operationId": "multiplePathGeneral", + "parameters": [ + { + "name": "key", + "in": "path", + "description": "The key of the minimap", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } } }, "components": { diff --git a/packages/bautajs-fastify-example/jest.config.js b/packages/bautajs-fastify-example/jest.config.js new file mode 100644 index 00000000..3378d6a7 --- /dev/null +++ b/packages/bautajs-fastify-example/jest.config.js @@ -0,0 +1,6 @@ +const config = require('@axa/bautajs-dev-config/jest.config.base'); + +module.exports = { + displayName: '@axa/bautajs-fastify-example', + ...config +}; diff --git a/packages/bautajs-fastify-example/package.json b/packages/bautajs-fastify-example/package.json index 71d46726..498d1ade 100644 --- a/packages/bautajs-fastify-example/package.json +++ b/packages/bautajs-fastify-example/package.json @@ -1,10 +1,11 @@ { "name": "@axa/bautajs-fastify-example", - "version": "2.2.2", + "version": "3.0.0-alpha.2", "description": "A bautaJS example in fastify", "main": "./server.js", "scripts": { - "start": "LOG_LEVEL=info DEBUG=bautajs* node ./server.js" + "start": "LOG_LEVEL=info DEBUG=bautajs* node ./server.js", + "test": "jest" }, "repository": { "type": "git", @@ -18,10 +19,9 @@ ], "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { - "@axa/bautajs-core": "^2.2.2", - "@axa/bautajs-datasource-rest": "^2.2.2", - "@axa/bautajs-decorator-cache": "^2.2.2", - "@axa/bautajs-fastify": "^2.2.2", - "fastify": "^4.8.1" + "@axa/bautajs-core": "^3.0.0-alpha.2", + "@axa/bautajs-datasource-rest": "^3.0.0-alpha.2", + "@axa/bautajs-fastify": "^3.0.0-alpha.2", + "fastify": "^4.15.0" } } diff --git a/packages/bautajs-fastify-example/server/datasources/cats-datasource.js b/packages/bautajs-fastify-example/server/datasources/cats-datasource.js deleted file mode 100644 index 2888f749..00000000 --- a/packages/bautajs-fastify-example/server/datasources/cats-datasource.js +++ /dev/null @@ -1,12 +0,0 @@ -const { restProvider } = require('@axa/bautajs-datasource-rest'); - -// Used to test that an https works -const catsRestProviderWithHttps = restProvider(client => { - return client.get('https://cat-fact.herokuapp.com/facts', { - https: { rejectUnauthorized: false } - }); -}); - -module.exports = { - catsRestProviderWithHttps -}; diff --git a/packages/bautajs-fastify-example/server/hooks/logger-hook.js b/packages/bautajs-fastify-example/server/hooks/logger-hook.js index 698e1578..cf8c33da 100644 --- a/packages/bautajs-fastify-example/server/hooks/logger-hook.js +++ b/packages/bautajs-fastify-example/server/hooks/logger-hook.js @@ -1,6 +1,6 @@ function logRequest(request, reply, done) { // eslint-disable-next-line no-console - console.log(`this would be a logRequest: ${request.url}`); + request.log.info(`logRequest: ${request.url}`); return done(); } diff --git a/packages/bautajs-fastify-example/server/resolvers/cache-resolver.js b/packages/bautajs-fastify-example/server/resolvers/cache-resolver.js index 41a51aa3..8d9a7be5 100644 --- a/packages/bautajs-fastify-example/server/resolvers/cache-resolver.js +++ b/packages/bautajs-fastify-example/server/resolvers/cache-resolver.js @@ -1,6 +1,5 @@ const { getRequest } = require('@axa/bautajs-fastify'); -const { pipe, resolver } = require('@axa/bautajs-core'); -const { cache } = require('@axa/bautajs-decorator-cache'); +const { cache, pipe, resolver } = require('@axa/bautajs-core'); const { chuckProvider } = require('../datasources/chuck-datasource'); const normalizer = (_, ctx) => { diff --git a/packages/bautajs-fastify-example/server/resolvers/cancel-request-resolver.js b/packages/bautajs-fastify-example/server/resolvers/cancel-request-resolver.js new file mode 100644 index 00000000..a17dc284 --- /dev/null +++ b/packages/bautajs-fastify-example/server/resolvers/cancel-request-resolver.js @@ -0,0 +1,43 @@ +const { resolver, pipe, step } = require('@axa/bautajs-core'); +const { getRequest } = require('@axa/bautajs-fastify'); + +const transformResponse = step(response => { + return { + message: response + }; +}); + +function getNumberFromRequestStep(_prev, ctx) { + const req = getRequest(ctx); + + const { number } = req.params; + + return number; +} + +function giveAnswerAfterWaitingWithTimeout() { + return step(async (number, ctx) => { + const timeout = number * 1000; + const promiseAnswer = new Promise(resolve => + setTimeout(() => { + resolve(`We have waited for ${number} seconds`); + }, timeout) + ); + const cancelTimeout = 3000; + const cancelator = new Promise(resolve => + setTimeout(() => { + ctx.token.cancel(); // If this triggers the promise does not resolve but it is cancelled + resolve('ended before the time!'); + }, cancelTimeout) + ); + + return Promise.race(await [promiseAnswer, cancelator]); + }); +} + +module.exports = resolver(operations => { + operations.cancelRequest + .validateRequest(false) + .validateResponse(false) + .setup(pipe(getNumberFromRequestStep, giveAnswerAfterWaitingWithTimeout(), transformResponse)); +}); diff --git a/packages/bautajs-fastify-example/server/resolvers/cats-resolver.js b/packages/bautajs-fastify-example/server/resolvers/cats-resolver.js deleted file mode 100644 index 8de9fd39..00000000 --- a/packages/bautajs-fastify-example/server/resolvers/cats-resolver.js +++ /dev/null @@ -1,6 +0,0 @@ -const { resolver } = require('@axa/bautajs-core'); -const { catsRestProviderWithHttps } = require('../datasources/cats-datasource'); - -module.exports = resolver(operations => { - operations.cats.validateRequest(false).validateResponse(false).setup(catsRestProviderWithHttps()); -}); diff --git a/packages/bautajs-fastify-example/server/resolvers/multiple-path-resolver.js b/packages/bautajs-fastify-example/server/resolvers/multiple-path-resolver.js new file mode 100644 index 00000000..a0d40d3d --- /dev/null +++ b/packages/bautajs-fastify-example/server/resolvers/multiple-path-resolver.js @@ -0,0 +1,32 @@ +const { pipe, step, resolver } = require('@axa/bautajs-core'); +const { getRequest } = require('@axa/bautajs-fastify'); + +const transformResponse = step(response => { + return { + message: response + }; +}); + +function generalStep(_prev, ctx) { + const req = getRequest(ctx); + + const { key } = req.params; + + return `This is the general text for requests and now we are receiving: ${key}`; +} + +function specificStep() { + return 'This is a simple text for requests to the specific path'; +} + +module.exports = resolver(operations => { + operations.multiplePathGeneral + .validateRequest(false) + .validateResponse(false) + .setup(pipe(generalStep, transformResponse)); + + operations.multiplePathSpecific + .validateRequest(false) + .validateResponse(false) + .setup(pipe(specificStep, transformResponse)); +}); diff --git a/packages/bautajs-fastify-example/test/fastify-example.test.js b/packages/bautajs-fastify-example/test/fastify-example.test.js new file mode 100644 index 00000000..f1cde688 --- /dev/null +++ b/packages/bautajs-fastify-example/test/fastify-example.test.js @@ -0,0 +1,193 @@ +const Fastify = require('fastify'); +const nock = require('nock'); + +const { registerFastifyServer } = require('../registrator'); + +describe('bautajs-fastify-example regressions tests', () => { + let fastify; + beforeAll(async () => { + nock.disableNetConnect(); + fastify = Fastify({ logger: true }); + await registerFastifyServer(fastify); + }); + + afterEach(() => {}); + + afterAll(() => { + nock.enableNetConnect(); + nock.cleanAll(); + fastify.close(); + }); + + test('GET /api/articles should return a successful response', async () => { + nock('https://jsonplaceholder.typicode.com').get(`/posts`).reply(200); + + const res = await fastify.inject({ + method: 'GET', + url: '/api/articles' + }); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/chuckfacts/:string should return a successful response', async () => { + const stringParam = 'foo'; + nock('https://api.chucknorris.io').get(`/jokes/search?query=${stringParam}`).reply(200); + + const res = await fastify.inject({ + method: 'GET', + url: `/api/chuckfacts/${stringParam}` + }); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/chuckfacts/:string should return a cached successful response', async () => { + const stringParam = 'bar'; + nock('https://api.chucknorris.io') + .get(`/jokes/search?query=${stringParam}`) + .reply(200, { fact: 'foo' }); + + const res = await fastify.inject({ + method: 'GET', + url: `/api/chuckfacts/${stringParam}` + }); + expect(res.statusCode).toBe(200); + expect(JSON.parse(res.body)).toEqual({ fact: 'foo' }); + expect(nock.isDone()).toBe(true); + + const res2 = await fastify.inject({ + method: 'GET', + url: `/api/chuckfacts/${stringParam}` + }); + expect(res2.statusCode).toBe(200); + expect(JSON.parse(res.body)).toEqual({ fact: 'foo' }); + }); + + test('GET api/cancel/:number should return a successful request for a number lower than 3', async () => { + const res = await fastify.inject({ + method: 'GET', + url: `/api/cancel/2` + }); + expect(res.statusCode).toBe(200); + }); + + test('GET api/cancel/:number should return a server error response for a number higher than 3', async () => { + const res = await fastify.inject({ + method: 'GET', + url: `/api/cancel/4` + }); + expect(res.statusCode).toBe(500); + }); + + test('minimap should create, get all and get by id successfully', async () => { + const resPost1 = await fastify.inject({ + method: 'POST', + url: `/api/minimap`, + body: { + key: 'farewell', + value: 'bye!' + } + }); + expect(resPost1.statusCode).toBe(200); + + const resGetById = await fastify.inject({ + method: 'GET', + url: `/api/minimap/farewell` + }); + expect(resGetById.statusCode).toBe(200); + expect(JSON.parse(resGetById.body)).toEqual({ + farewell: 'bye!' + }); + + const resPost2 = await fastify.inject({ + method: 'POST', + url: `/api/minimap`, + body: { + key: 'hi', + value: 'hola!' + } + }); + expect(resPost2.statusCode).toBe(200); + + const resGetAll = await fastify.inject({ + method: 'GET', + url: `/api/minimap` + }); + expect(resGetAll.statusCode).toBe(200); + expect(JSON.parse(resGetAll.body)).toEqual({ + farewell: 'bye!', + hi: 'hola!' + }); + }); + + test('GET /api/multiple-path/{key} should return a successful response', async () => { + const res = await fastify.inject({ + method: 'GET', + url: `/api/multiple-path/hola` + }); + + expect(res.statusCode).toBe(200); + expect(JSON.parse(res.body)).toEqual({ + message: 'This is the general text for requests and now we are receiving: hola' + }); + }); + + test('GET /api/multiple-path/specific should return a successful response', async () => { + const res = await fastify.inject({ + method: 'GET', + url: `/api/multiple-path/specific` + }); + + expect(res.statusCode).toBe(200); + expect(JSON.parse(res.body)).toEqual({ + message: 'This is a simple text for requests to the specific path' + }); + }); + + test('GET api/randomYear should return a successful response', async () => { + nock('http://numbersapi.com').get('/random/year?json').reply(200); + + const res = await fastify.inject({ + method: 'GET', + url: `/api/randomYear` + }); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/randomYear should return a successful response', async () => { + nock('http://numbersapi.com').get('/random/year?json').reply(200); + + const res = await fastify.inject({ + method: 'GET', + url: `/api/randomYear2` + }); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/factNumber/:number should return successful response', async () => { + const stringParam = '100'; + nock('http://numbersapi.com').get(`/${stringParam}/math`).reply(200); + + const res = await fastify.inject({ + method: 'GET', + url: `/api/factNumber/${stringParam}` + }); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); + + test('GET api/factNumber2/:number should return successful response', async () => { + const stringParam = '100'; + nock('http://numbersapi.com').get(`/${stringParam}/math`).reply(200); + + const res = await fastify.inject({ + method: 'GET', + url: `/api/factNumber2/${stringParam}` + }); + expect(res.statusCode).toBe(200); + expect(nock.isDone()).toBe(true); + }); +}); diff --git a/packages/bautajs-fastify/README.md b/packages/bautajs-fastify/README.md index c30a1558..46ffc32c 100644 --- a/packages/bautajs-fastify/README.md +++ b/packages/bautajs-fastify/README.md @@ -68,8 +68,7 @@ Licensed under the (MIT / Apache 2.0) License. - [@fastify/swagger@7.4.1](https://github.com/fastify/fastify-swagger) - MIT - [fastify-plugin@4.2.1](https://github.com/fastify/fastify-plugin) - MIT - [fastify-x-request-id@2.0.0](https://github.com/dimonnwc3/fastify-x-request-id) - MIT - - [route-order@0.1.0](https://github.com/sfrdmn/node-route-order) - MIT - + ## Development - [@axa/bautajs-datasource-rest@5.1.2](https://github.com/axa-group/bauta.js) - MIT* @@ -80,7 +79,6 @@ Licensed under the (MIT / Apache 2.0) License. - [fastify@4.5.3](https://github.com/fastify/fastify) - MIT - [@fastify/swagger@7.4.1](https://github.com/fastify/fastify-swagger) - MIT - [fastify-plugin@4.2.1](https://github.com/fastify/fastify-plugin) - MIT - - [route-order@0.1.0](https://github.com/sfrdmn/node-route-order) - MIT ### Development - [@axa/bautajs-datasource-rest@1.0.0](https://github.com/axa-group/bauta.js) - MIT* diff --git a/packages/bautajs-fastify/examples/simple-usage/resolvers/v1/source/cats-datasource.js b/packages/bautajs-fastify/examples/simple-usage/resolvers/v1/source/cats-datasource.js deleted file mode 100644 index 17cf6020..00000000 --- a/packages/bautajs-fastify/examples/simple-usage/resolvers/v1/source/cats-datasource.js +++ /dev/null @@ -1,12 +0,0 @@ -const { restProvider } = require('@axa/bautajs-datasource-rest'); - -// Used to test that an https works -const catsRestProviderWithHttps = restProvider(client => { - return client.get('https://cat-fact.herokuapp.com/facts', { - rejectUnauthorized: false - }); -}); - -module.exports = { - catsRestProviderWithHttps -}; diff --git a/packages/bautajs-fastify/examples/simple-usage/resolvers/v1/source/numbers-resolver.js b/packages/bautajs-fastify/examples/simple-usage/resolvers/v1/source/numbers-resolver.js index 48d8387f..4cde657d 100644 --- a/packages/bautajs-fastify/examples/simple-usage/resolvers/v1/source/numbers-resolver.js +++ b/packages/bautajs-fastify/examples/simple-usage/resolvers/v1/source/numbers-resolver.js @@ -1,6 +1,5 @@ const { resolver, step, pipe } = require('@axa/bautajs-core'); const { exampleRestProviderYear, exampleRestProvider } = require('./numbers-datasource'); -const { catsRestProviderWithHttps } = require('./cats-datasource'); const transformResponse = step(response => { return { @@ -15,6 +14,4 @@ module.exports = resolver(operations => { operations.factNumber.setup(pipe(exampleRestProvider(), transformResponse)); operations.factNumber2.setup(pipe(exampleRestProvider(), transformResponse)); - - operations.cats.setup(pipe(catsRestProviderWithHttps(), transformResponse)); }); diff --git a/packages/bautajs-fastify/jest.config.js b/packages/bautajs-fastify/jest.config.js index 967eae9d..4961fd7e 100644 --- a/packages/bautajs-fastify/jest.config.js +++ b/packages/bautajs-fastify/jest.config.js @@ -1,3 +1,6 @@ -const config = require('../../jest.config.base'); +const config = require('@axa/bautajs-dev-config/jest.config.base'); -module.exports = { displayName: '@axa/bautajs-fastify', ...config }; +module.exports = { + displayName: '@axa/bautajs-fastify', + ...config +}; diff --git a/packages/bautajs-fastify/package.json b/packages/bautajs-fastify/package.json index 69293b5b..b8f64717 100644 --- a/packages/bautajs-fastify/package.json +++ b/packages/bautajs-fastify/package.json @@ -1,6 +1,6 @@ { "name": "@axa/bautajs-fastify", - "version": "2.2.2", + "version": "3.0.0-alpha.2", "description": "A bautaJS fastify plugin. Allows auto exposing the bautaJS operations.", "main": "dist", "types": "dist", @@ -9,6 +9,9 @@ ], "scripts": { "tsc": "tsc", + "build": "tsc --build", + "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist", + "test": "jest", "example": "node ./examples/simple-usage/index.js" }, "repository": { @@ -25,18 +28,18 @@ "middleware" ], "engines": { - "node": ">=14", + "node": ">=16", "npm": ">=8" }, "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { - "@axa/bautajs-core": "^2.2.2", + "@axa/bautajs-core": "^3.0.0-alpha.2", "@fastify/swagger": "^7.6.1", - "fastify": "^4.8.1", - "fastify-plugin": "^4.2.1", - "route-order": "^0.1.0" + "fastify": "^4.15.0", + "fastify-plugin": "^4.5.0" }, "devDependencies": { - "@axa/bautajs-datasource-rest": "^2.2.2" + "@axa/bautajs-datasource-rest": "^3.0.0-alpha.2", + "@axa/bautajs-dev-config": "*" } } diff --git a/packages/bautajs-fastify/src/expose-routes.ts b/packages/bautajs-fastify/src/expose-routes.ts index dc618555..b00d6cff 100644 --- a/packages/bautajs-fastify/src/expose-routes.ts +++ b/packages/bautajs-fastify/src/expose-routes.ts @@ -1,7 +1,6 @@ import * as fastify from 'fastify'; import path from 'path'; import { Operation, ValidationError, Validator, LocationError } from '@axa/bautajs-core'; -import routeOrder from 'route-order'; import { ApiHooks, OnResponseValidationError } from './types'; // We are using ajv 8 and dataPath is moved to instancePath @@ -42,7 +41,7 @@ function createHandler(operation: Operation) { const op = operation.run<{ req: fastify.FastifyRequest; res: fastify.FastifyReply }, any>({ req: request, res: reply, - id: request.id || request.headers['x-request-id'], + id: (request.id || request.headers['x-request-id']) as string, url: request.url, log: request.log }); @@ -204,12 +203,10 @@ async function exposeRoutes( ); } - Object.keys(opts.routes) - .sort(routeOrder()) - .forEach(route => { - const methods = Object.keys(opts.routes[route]); - methods.forEach(method => addRoute(opts.routes[route][method], opts.validator)); - }); + Object.keys(opts.routes).forEach(route => { + const methods = Object.keys(opts.routes[route]); + methods.forEach(method => addRoute(opts.routes[route][method], opts.validator)); + }); } export default exposeRoutes; diff --git a/packages/bautajs-fastify/src/__tests__/fastify-inheritance.test.ts b/packages/bautajs-fastify/test/fastify-inheritance.test.ts similarity index 98% rename from packages/bautajs-fastify/src/__tests__/fastify-inheritance.test.ts rename to packages/bautajs-fastify/test/fastify-inheritance.test.ts index 16d431f1..60003ae7 100644 --- a/packages/bautajs-fastify/src/__tests__/fastify-inheritance.test.ts +++ b/packages/bautajs-fastify/test/fastify-inheritance.test.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line no-unused-vars import { BautaJS } from '@axa/bautajs-core'; import fastify, { FastifyInstance } from 'fastify'; -import { bautajsFastify } from '../index'; +import { bautajsFastify } from '../src/index'; const [ apiDefinition, diff --git a/packages/bautajs-fastify/src/__tests__/fastify-validation.test.ts b/packages/bautajs-fastify/test/fastify-validation.test.ts similarity index 99% rename from packages/bautajs-fastify/src/__tests__/fastify-validation.test.ts rename to packages/bautajs-fastify/test/fastify-validation.test.ts index c05405e5..22e431a3 100644 --- a/packages/bautajs-fastify/src/__tests__/fastify-validation.test.ts +++ b/packages/bautajs-fastify/test/fastify-validation.test.ts @@ -1,7 +1,7 @@ import path from 'path'; import fastify, { FastifyInstance } from 'fastify'; import { ValidationError } from '@axa/bautajs-core'; -import { bautajsFastify } from '../index'; +import { bautajsFastify } from '../src/index'; const apiDefinitionCustomValidation = require('./fixtures/test-api-definitions-custom-validation.json'); diff --git a/packages/bautajs-fastify/src/__tests__/fastify.test.ts b/packages/bautajs-fastify/test/fastify.test.ts similarity index 87% rename from packages/bautajs-fastify/src/__tests__/fastify.test.ts rename to packages/bautajs-fastify/test/fastify.test.ts index 395ea0fe..134940ba 100644 --- a/packages/bautajs-fastify/src/__tests__/fastify.test.ts +++ b/packages/bautajs-fastify/test/fastify.test.ts @@ -3,12 +3,12 @@ import FormData from 'form-data'; import { Readable } from 'stream'; import { resolver, defaultLogger, pipe, asPromise } from '@axa/bautajs-core'; import fastify, { FastifyInstance } from 'fastify'; -import { bautajsFastify } from '../index'; -import { getRequest, getResponse } from '../operators'; +import { bautajsFastify, getRequest, getResponse } from '../src/index'; const apiDefinition = require('./fixtures/test-api-definitions.json'); const apiDefinitionSwagger2 = require('./fixtures/test-api-definitions-swagger-2.json'); const apiDefinitionSwaggerWithExtraTag = require('./fixtures/test-api-definition-extra-tag.json'); +const apiDefinitionSwaggerPathOrdering = require('./fixtures/test-api-definitions-swagger-path-ordering.json'); describe('bautaJS fastify tests', () => { describe('bautaJS fastify generic test', () => { @@ -481,9 +481,9 @@ describe('bautaJS fastify tests', () => { test('should log a message in case of the request was canceled', async () => { const logger = defaultLogger(); jest.spyOn(logger, 'error').mockImplementation(); - logger.child = () => { + logger.child = (() => { return logger; - }; + }) as any; const fs = fastify({ logger }); fs.register(bautajsFastify, { apiBasePath: '/api/', @@ -595,4 +595,74 @@ describe('bautaJS fastify tests', () => { expect(res2.statusCode).toBe(302); }); }); + + describe('path ordering', () => { + let fastifyInstance: FastifyInstance; + beforeEach(() => { + fastifyInstance = fastify(); + }); + afterEach(() => { + fastifyInstance.close(); + }); + + test('should work with specific operation first in the resolver', async () => { + fastifyInstance.register(bautajsFastify, { + apiDefinition: apiDefinitionSwaggerPathOrdering, + resolversPath: path.resolve( + __dirname, + './fixtures/test-resolvers/path-ordering-specific-first-resolver.js' + ), + apiBasePath: '/api/', + prefix: '/v1/' + }); + + const generalResponse = await fastifyInstance.inject({ + method: 'GET', + url: '/v1/api/multiple-path/cat' + }); + + expect(JSON.parse(generalResponse.payload)).toStrictEqual({ + message: 'This is the general text for requests and now we are receiving: cat' + }); + + const specificResponse = await fastifyInstance.inject({ + method: 'GET', + url: '/v1/api/multiple-path/specific' + }); + + expect(JSON.parse(specificResponse.payload)).toStrictEqual({ + message: 'This is a simple text for requests to the specific path' + }); + }); + + test('should work with general operation first in the resolver', async () => { + fastifyInstance.register(bautajsFastify, { + apiDefinition: apiDefinitionSwaggerPathOrdering, + resolversPath: path.resolve( + __dirname, + './fixtures/test-resolvers/path-ordering-general-first-resolver.js' + ), + apiBasePath: '/api/', + prefix: '/v1/' + }); + + const generalResponse = await fastifyInstance.inject({ + method: 'GET', + url: '/v1/api/multiple-path/cat' + }); + + expect(JSON.parse(generalResponse.payload)).toStrictEqual({ + message: 'This is the general text for requests and now we are receiving: cat' + }); + + const specificResponse = await fastifyInstance.inject({ + method: 'GET', + url: '/v1/api/multiple-path/specific' + }); + + expect(JSON.parse(specificResponse.payload)).toStrictEqual({ + message: 'This is a simple text for requests to the specific path' + }); + }); + }); }); diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-api-definition-extra-tag.json b/packages/bautajs-fastify/test/fixtures/test-api-definition-extra-tag.json similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-api-definition-extra-tag.json rename to packages/bautajs-fastify/test/fixtures/test-api-definition-extra-tag.json diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-api-definition-inheritance.json b/packages/bautajs-fastify/test/fixtures/test-api-definition-inheritance.json similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-api-definition-inheritance.json rename to packages/bautajs-fastify/test/fixtures/test-api-definition-inheritance.json diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-api-definitions-custom-validation.json b/packages/bautajs-fastify/test/fixtures/test-api-definitions-custom-validation.json similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-api-definitions-custom-validation.json rename to packages/bautajs-fastify/test/fixtures/test-api-definitions-custom-validation.json diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-api-definitions-swagger-2.json b/packages/bautajs-fastify/test/fixtures/test-api-definitions-swagger-2.json similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-api-definitions-swagger-2.json rename to packages/bautajs-fastify/test/fixtures/test-api-definitions-swagger-2.json diff --git a/packages/bautajs-fastify/test/fixtures/test-api-definitions-swagger-path-ordering.json b/packages/bautajs-fastify/test/fixtures/test-api-definitions-swagger-path-ordering.json new file mode 100644 index 00000000..85f4eee1 --- /dev/null +++ b/packages/bautajs-fastify/test/fixtures/test-api-definitions-swagger-path-ordering.json @@ -0,0 +1,84 @@ + +{ + "openapi": "3.0.0", + "info": { + "version": "v1", + "title": "Swagger Petstore", + "license": { + "name": "MIT" + } + }, + "paths": { + "/multiple-path/specific": { + "get": { + "summary": "Specific endpoint to test path collision and route ordering", + "operationId": "multiplePathSpecific", + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/multiple-path/{key}": { + "get": { + "summary": "General endpoint to test path collision and route ordering", + "operationId": "multiplePathGeneral", + "parameters": [ + { + "name": "key", + "in": "path", + "description": "The key of the minimap", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Something!" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Error": { + "required": [ + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-api-definitions.json b/packages/bautajs-fastify/test/fixtures/test-api-definitions.json similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-api-definitions.json rename to packages/bautajs-fastify/test/fixtures/test-api-definitions.json diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-resolvers/operation-resolver-error.js b/packages/bautajs-fastify/test/fixtures/test-resolvers/operation-resolver-error.js similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-resolvers/operation-resolver-error.js rename to packages/bautajs-fastify/test/fixtures/test-resolvers/operation-resolver-error.js diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-resolvers/operation-resolver-invalid.js b/packages/bautajs-fastify/test/fixtures/test-resolvers/operation-resolver-invalid.js similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-resolvers/operation-resolver-invalid.js rename to packages/bautajs-fastify/test/fixtures/test-resolvers/operation-resolver-invalid.js diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-resolvers/operation-resolver.js b/packages/bautajs-fastify/test/fixtures/test-resolvers/operation-resolver.js similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-resolvers/operation-resolver.js rename to packages/bautajs-fastify/test/fixtures/test-resolvers/operation-resolver.js diff --git a/packages/bautajs-fastify/test/fixtures/test-resolvers/path-ordering-general-first-resolver.js b/packages/bautajs-fastify/test/fixtures/test-resolvers/path-ordering-general-first-resolver.js new file mode 100644 index 00000000..368905be --- /dev/null +++ b/packages/bautajs-fastify/test/fixtures/test-resolvers/path-ordering-general-first-resolver.js @@ -0,0 +1,32 @@ +const { pipe, step, resolver } = require('@axa/bautajs-core'); +const { getRequest } = require('../../../src/index'); + +const transformResponse = step(response => { + return { + message: response + }; +}); + +function generalStep(_prev, ctx) { + const req = getRequest(ctx); + + const { key } = req.params; + + return `This is the general text for requests and now we are receiving: ${key}`; +} + +function specificStep() { + return 'This is a simple text for requests to the specific path'; +} + +module.exports = resolver(operations => { + operations.multiplePathGeneral + .validateRequest(false) + .validateResponse(false) + .setup(pipe(generalStep, transformResponse)); + + operations.multiplePathSpecific + .validateRequest(false) + .validateResponse(false) + .setup(pipe(specificStep, transformResponse)); +}); diff --git a/packages/bautajs-fastify/test/fixtures/test-resolvers/path-ordering-specific-first-resolver.js b/packages/bautajs-fastify/test/fixtures/test-resolvers/path-ordering-specific-first-resolver.js new file mode 100644 index 00000000..20fa97c9 --- /dev/null +++ b/packages/bautajs-fastify/test/fixtures/test-resolvers/path-ordering-specific-first-resolver.js @@ -0,0 +1,32 @@ +const { pipe, step, resolver } = require('@axa/bautajs-core'); +const { getRequest } = require('../../../src/index'); + +const transformResponse = step(response => { + return { + message: response + }; +}); + +function generalStep(_prev, ctx) { + const req = getRequest(ctx); + + const { key } = req.params; + + return `This is the general text for requests and now we are receiving: ${key}`; +} + +function specificStep() { + return 'This is a simple text for requests to the specific path'; +} + +module.exports = resolver(operations => { + operations.multiplePathSpecific + .validateRequest(false) + .validateResponse(false) + .setup(pipe(specificStep, transformResponse)); + + operations.multiplePathGeneral + .validateRequest(false) + .validateResponse(false) + .setup(pipe(generalStep, transformResponse)); +}); diff --git a/packages/bautajs-fastify/src/__tests__/fixtures/test-resolvers/private-operation-resolver.js b/packages/bautajs-fastify/test/fixtures/test-resolvers/private-operation-resolver.js similarity index 100% rename from packages/bautajs-fastify/src/__tests__/fixtures/test-resolvers/private-operation-resolver.js rename to packages/bautajs-fastify/test/fixtures/test-resolvers/private-operation-resolver.js diff --git a/packages/bautajs-fastify/tsconfig.json b/packages/bautajs-fastify/tsconfig.json index a750b029..a21e6276 100644 --- a/packages/bautajs-fastify/tsconfig.json +++ b/packages/bautajs-fastify/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base", + "extends": "@axa/bautajs-dev-config/tsconfig.base.json", "compilerOptions": { "rootDir": "./src", "outDir": "./dist" diff --git a/tsconfig.build.json b/tsconfig.build.json deleted file mode 100644 index 121fd972..00000000 --- a/tsconfig.build.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "compilerOptions": { - "composite":true, - "typeRoots": ["node_modules/@types", "./types"] - }, - "files": [], - "include": ["src/**/*.ts", "src/**/*.json"], - "exclude": ["**/__tests__"], - "references": [ - { "path": "./packages/bautajs-core" }, - { "path": "./packages/bautajs-datasource-rest" }, - { "path": "./packages/bautajs-decorator-cache" }, - { "path": "./packages/bautajs-express" }, - { "path": "./packages/bautajs-fastify" } - ] -} diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 00000000..496b5f23 --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,3 @@ +{ + "extends": "@axa/bautajs-dev-config/tsconfig.base.json" +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 35c5c2aa..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./tsconfig.base.json", - "files": [], - "compilerOptions": { - "target": "es6" - }, - "references": [ - { "path": "./tsconfig.build.json" }, - { "path": "./tsconfig.test.json" }, - ] -} \ No newline at end of file diff --git a/turbo.json b/turbo.json new file mode 100644 index 00000000..5a3847d8 --- /dev/null +++ b/turbo.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + + }, + "test": { + "dependsOn": ["build"] + }, + "clean": { + "cache": false + } + } +} \ No newline at end of file diff --git a/types/route-order.d.ts b/types/route-order.d.ts deleted file mode 100644 index 24936f1e..00000000 --- a/types/route-order.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'route-order';