[
  {
    "path": ".eslintignore",
    "content": ""
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  parser: '@typescript-eslint/parser',\n  parserOptions: {\n    project: 'tsconfig.eslint.json',\n    sourceType: 'module',\n  },\n  plugins: ['@typescript-eslint/eslint-plugin'],\n  extends: ['plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],\n  root: true,\n  env: {\n    node: true,\n    jest: true,\n  },\n  ignorePatterns: ['.eslintrc.js'],\n  rules: {\n    '@typescript-eslint/interface-name-prefix': 'off',\n    '@typescript-eslint/explicit-function-return-type': 'off',\n    '@typescript-eslint/no-explicit-any': 'off',\n    '@typescript-eslint/no-empty-function': 'off',\n    '@typescript-eslint/no-non-null-assertion': 'off',\n    '@typescript-eslint/no-var-requires': 'off',\n    'lines-between-class-members': ['error', 'always'],\n    'padded-blocks': ['error', 'never'],\n    semi: ['error', 'always'],\n    quotes: ['error', 'single', { avoidEscape: true }],\n    'max-len': ['error', { code: 150, comments: 200 }],\n    'comma-dangle': ['error', 'always-multiline'],\n    '@typescript-eslint/no-namespace': 'off',\n    '@typescript-eslint/member-ordering': [\n      'error',\n      {\n        default: [\n          'signature',\n          'public-static-field',\n          'protected-static-field',\n          'private-static-field',\n          'public-instance-field',\n          'protected-instance-field',\n          'private-instance-field',\n          'public-abstract-field',\n          'protected-abstract-field',\n          'private-abstract-field',\n          'public-constructor',\n          'protected-constructor',\n          'private-constructor',\n          'public-static-method',\n          'protected-static-method',\n          'private-static-method',\n          'public-instance-method',\n          'protected-instance-method',\n          'private-instance-method',\n          'public-abstract-method',\n          'protected-abstract-method',\n          'private-abstract-method',\n        ],\n      },\n    ],\n  },\n};\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: nestjsx\nko_fi: # Replace with a single Ko-fi username\ntidelift: npm/@nestjsx/crud\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: # Replace with a single custom sponsorship URL\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Bug_report.md",
    "content": "---\nname: \"\\U0001F41B Bug Report\"\nabout: \"If something isn't working as expected \\U0001F914.\"\ntitle: ''\nlabels: 'type: potential issue'\nassignees: ''\n---\n\n## Bug Report\n\n## Current behavior\n\n<!-- Describe how the issue manifests. -->\n\n## Input Code\n\n<!-- REPL or Repo link if applicable: -->\n\n```ts\nconst your = (code) => here;\n```\n\n## Expected behavior\n\n<!-- A clear and concise description of what you expected to happen (or code). -->\n\n## Possible Solution\n\n<!--- Only if you have suggestions on a fix for the bug -->\n\n## Environment\n\n<pre><code>\nPackage version: X.Y.Z\n<!-- Check whether this is still an issue in the most recent package(s) version -->\n \nFor Tooling issues:\n- Node version: XX  <!-- run `node --version` -->\n- Platform:  <!-- Mac, Linux, Windows -->\n- Database <!-- MySQL, Postgres, etc. (with version) -->\n\nOthers:\n<!-- Anything else relevant?  Operating system version, IDE, ... -->\n</code></pre>\n\n## Repository with minimal reproduction\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/Feature_request.md",
    "content": "---\nname: \"\\U0001F680 Feature Request\"\nabout: \"I have a suggestion \\U0001F63B!\"\ntitle: ''\nlabels: 'type: enhancement'\nassignees: ''\n---\n\n## Feature Request\n\n## Is your feature request related to a problem? Please describe.\n\n<!-- A clear and concise description of what the problem is. Ex. I have an issue when [...] -->\n\n## Describe the solution you'd like\n\n<!-- A clear and concise description of what you want to happen. Add any considered drawbacks. -->\n\n## Teachability, Documentation, Adoption, Migration Strategy\n\n<!-- If you can, explain how users will be able to use this and possibly write out a version the docs. Maybe a screenshot or design? -->\n\n## What is the motivation / use case for changing the behavior?\n\n<!-- Describe the motivation or the concrete use case. -->\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "## PR Checklist\n\nPlease check if your PR fulfills the following requirements:\n\n- [ ] The commit message was generated by `yarn commit`\n- [ ] Tests for the changes have been added (for bug fixes / features)\n\n## PR Type\n\nWhat kind of change does this PR introduce?\n\n<!-- Please check the one that applies to this PR using \"x\". -->\n\n```\n[ ] Bugfix\n[ ] Feature\n[ ] Code style update (formatting, local variables)\n[ ] Refactoring (no functional changes, no api changes)\n[ ] Build related changes\n[ ] Tests\n[ ] Release\n[ ] CI related changes\n[ ] Other... Please describe:\n```\n\n## What is the current behavior?\n\n<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->\n\nIssue Number: N/A\n\n## What is the new behavior?\n\n## Does this PR introduce a breaking change?\n\n```\n[ ] Yes\n[ ] No\n```\n\n<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->\n\n## Other information\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  pull_request:\n    types: [closed]\n\njobs:\n  release:\n    if: github.event.pull_request.merged && startsWith(github.head_ref, 'release')\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n        with:\n          fetch-depth: '0'\n\n      - name: Set NPM Token\n        uses: actions/setup-node@v2\n        with:\n          registry-url: https://registry.npmjs.org\n        env:\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n\n      - name: Install\n        run: yarn boot\n\n      - name: Build\n        run: yarn build\n\n      - name: Run Tests\n        run: yarn test\n\n      - name: Get Latest Tag\n        id: get_latest_tag\n        uses: actions-ecosystem/action-get-latest-tag@v1\n\n      - name: Parse Latest Git Tag\n        id: parse_latest_tag\n        run: |\n          TAG=$(echo ${{ steps.get_latest_tag.outputs.tag }} | cut -d 'v' -f 2)\n          echo \"::set-output name=tag::${TAG}\"\n\n      - name: Get Next Tag Semver\n        id: get_next_tag_semver\n        uses: WyriHaximus/github-action-next-semvers@v1\n        with:\n          version: ${{ steps.parse_latest_tag.outputs.tag }}\n\n      - name: Define Publishing Strategy\n        id: lerna_strategy\n        run: |\n          MREPO_LERNA_STRATEGY=$(cat lerna.json | npx jase version)\n          echo \"MREPO_LERNA_STRATEGY=${MREPO_LERNA_STRATEGY}\" >> $GITHUB_ENV\n\n      - name: Define Current Version\n        run: |\n          echo ${{ env.MREPO_LERNA_STRATEGY }}\n          if [ \"${{ env.MREPO_LERNA_STRATEGY }}\" = \"independent\" ]; then\n            CURRENT_VERSION=${{ steps.get_next_tag_semver.outputs.v_minor }}\n            DIST_TAG=$(cat lerna.json | npx jase distTag)\n          else\n            CURRENT_VERSION=$(cat lerna.json | npx jase version)\n            [[ $CURRENT_VERSION =~ \"-\" ]] && DIST_TAG=$(echo $CURRENT_VERSION | cut -d '-' -f 2 | cut -d '.' -f 1) || DIST_TAG=\"latest\"\n            CURRENT_VERSION=v$CURRENT_VERSION\n          fi\n\n          echo $CURRENT_VERSION\n          echo $DIST_TAG\n\n          echo \"CURRENT_VERSION=${CURRENT_VERSION}\" >> $GITHUB_ENV\n          echo \"DIST_TAG=${DIST_TAG}\" >> $GITHUB_ENV\n\n      - name: Ensure Latest Git Tag\n        uses: mukunku/tag-exists-action@v1.0.0\n        id: checkTag\n        with:\n          tag: ${{ env.CURRENT_VERSION }}\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Create Git Tag\n        if: steps.checkTag.outputs.exists == 'false'\n        uses: negz/create-tag@v1\n        with:\n          version: ${{ env.CURRENT_VERSION }}\n          token: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Create Release\n        if: steps.checkTag.outputs.exists == 'false'\n        uses: actions/create-release@v1\n        with:\n          tag_name: ${{ env.CURRENT_VERSION }}\n          release_name: ${{ env.CURRENT_VERSION }}\n          body: |\n            ${{ github.event.pull_request.body }}\n          draft: false\n          prerelease: false\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Publish Packages to NPM Registry\n        if: steps.checkTag.outputs.exists == 'false'\n        run: |\n          lerna publish from-package --no-git-reset --no-verify-access --yes --registry https://registry.npmjs.org --dist-tag ${{ env.DIST_TAG }}\n          git update-index --assume-unchanged ./packages/**/package.json\n        env:\n          GH_TOKEN: ${{ secrets.NPM_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n\n      - name: Create a Comment\n        uses: actions/github-script@0.8.0\n        with:\n          github-token: ${{secrets.GITHUB_TOKEN}}\n          script: |\n            github.issues.createComment({\n              issue_number: context.issue.number,\n              owner: context.repo.owner,\n              repo: context.repo.repo,\n              body: 'Packages with version [${{ env.CURRENT_VERSION }}](https://github.com/${{ github.repository }}/releases/tag/${{ env.CURRENT_VERSION }}) have been released 🎉'\n            })\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Tests\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\n  workflow_dispatch:\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    env:\n      COMPOSE_FILE: ./docker-compose.yml\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - name: build docker db\n        run: docker-compose up -d\n\n      - name: install\n        run: yarn boot\n\n      - name: build\n        run: yarn build\n\n      - name: check docker\n        run: docker-compose up -d\n\n      - name: tests\n        run: yarn test:coverage\n\n      - name: Coveralls Parallel\n        uses: coverallsapp/github-action@master\n        with:\n          github-token: ${{ secrets.github_token }}\n          flag-name: run-${{ matrix.test_number }}\n          parallel: true\n\n  coverage:\n    needs: test\n    runs-on: ubuntu-latest\n    steps:\n      - name: Coveralls coverage\n        uses: coverallsapp/github-action@master\n        with:\n          github-token: ${{ secrets.github_token }}\n          parallel-finished: true\n"
  },
  {
    "path": ".gitignore",
    "content": "# dependencies\n/**/node_modules\nyarn-error.log\nnpm-debug.log\n\n# IDE\n/.idea\n/.awcache\n/.vscode\n\n# misc\n.DS_Store\nlerna-debug.log\n.npmrc\n.nvmrc\n\n# tests\n/coverage\n/.nyc_output\n\n# dist\npackages/**/lib\npackages/**/tsconfig.tsbuildinfo\n.mrepo\n"
  },
  {
    "path": ".husky/pre-commit",
    "content": "#!/bin/sh\n. \"$(dirname \"$0\")/_/husky.sh\"\n\nyarn lint && yarn format\n"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n  \"printWidth\": 120,\n  \"singleQuote\": true,\n  \"trailingComma\": \"all\",\n  \"arrowParens\": \"always\"\n}\n"
  },
  {
    "path": ".yarnrc",
    "content": "save-prefix \"\"\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## [4.6.2] - 2020-05-14\n\n### Bug Fixes\n\n- **typeorm** - fixed selected fields on joins [#510](https://github.com/nestjsx/crud/issues/510)). Kudos to @jbrousseau for finding this bug\n\n## [4.6.1] - 2020-05-08\n\n### Bug Fixes\n\n- **typeorm** - fixed query generation when a column display name differs from its name in db [#401](https://github.com/nestjsx/crud/issues/401)). Kudos to @farhad2161 for finding this bug\n\n## [4.6.0] - 2020-05-07\n\n### Features\n\n- **crud**/**typeorm** - added `select` (boolean) to `join` options which allows to join relation but not select it ([#218](https://github.com/nestjsx/crud/issues/218))\n\n### Bug Fixes\n\n- **typeorm** - fixed column identifier for MySQL ([#401](https://github.com/nestjsx/crud/issues/401))\n- **typeorm** - fixed nested relations aliases, filtering, sorting ([#419](https://github.com/nestjsx/crud/issues/419), [#450](https://github.com/nestjsx/crud/issues/450), [#267](https://github.com/nestjsx/crud/issues/267), [#385](https://github.com/nestjsx/crud/issues/385))\n\n## [4.5.0] - 2020-05-01\n\n### Improvements\n\n- **crud** - added enum support for params Swagger. Kudos to @tbrannam\n\n### Bug Fixes\n\n- **crud** - fixed auth property definition. Kudos to @lafeuil\n- **typeorm** - fixed request generation with aliases ([#321](https://github.com/nestjsx/crud/issues/321), [#401](https://github.com/nestjsx/crud/issues/401)). Kudos to @joennlae\n\n## [4.4.5] - 2020-04-18\n\n## Deps\n\n- **crud** fixted imports\n\n## [4.4.4] - 2020-04-18\n\n### Deps\n\n- **dev** fixted lerna\n\n## [4.4.3] - 2020-04-18\n\n### Bug Fixes\n\n- **crud** fixed returning `pageCount` in some cases ([#465](https://github.com/nestjsx/crud/pull/465))\n- **typeorm** fixed critical bug with possible SQL injections when using query `?sort=` (big kudos to João Maurício)\n- **typeorm** fixed filter conditions for LIKE/iLIKE operators ([#395](https://github.com/nestjsx/crud/pull/395))\n\n## [4.4.2] - 2020-03-17\n\n### Bug Fixes\n\n- **crud** fixed custom routes params caused by NestJs v7 breaking changes ([#443](https://github.com/nestjsx/crud/issues/443))\n\n## [4.4.1] - 2019-12-28\n\n### Bug Fixes\n\n- **crud** fixed `CrudRequestInterceptor` validation status code from 500 to 400 ([#374](https://github.com/nestjsx/crud/issues/374), [#247](https://github.com/nestjsx/crud/issues/247))\n\n## [4.4.0] - 2019-12-27\n\n### Features\n\n- **crud** added `serialize` to the global options\n\n## [4.3.0] - 2019-12-21\n\n### Features\n\n- **crud** added `dto` to the `CrudOptions` ([#132](https://github.com/nestjsx/crud/issues/132))\n- **crud** added `serialize` to the `CrudOptions`\n- **crud** added `search` query param and a new search condition api\n- **crud** added new condition operators: `$eqL`, `$neL`, `$startsL`, `$endsL`, `$contL`, `$exclL`, `$inL`, `$notinL` for case insensitive queries ([#77](https://github.com/nestjsx/crud/issues/77))\n- **crud** added `@crudAuth()` class decorator for authorized requests\n\n### Improvements\n\n- **crud** `CrudRequestInterceptor` can be used for both crud and non-crud controllers or for custom routes within crud controller\n- **crud** support `@nestjs/swagger` major versions: v3 and v4 ([#340](https://github.com/nestjsx/crud/issues/340))\n- **crud** added `returnShallow` option to the `CrudOptions.routes` `createOneBase`, `updateOneBase`, `replaceOneBase` methods ([#158](https://github.com/nestjsx/crud/issues/158))\n- **crud** added `alias` to the `CrudOptions.join` ([#350](https://github.com/nestjsx/crud/issues/55))\n- **crud** added `alwaysPaginate` to the `CrudOptions.query`, can be used globally as well ([#213](https://github.com/nestjsx/crud/issues/213))\n- **crud** `CrudOptions.query.filter` can be a function that returns transformed `search` object\n- **crud** added `disabled` for an objects withing `CrudOptions.params`\n- **request** query builder: now uses [qs](https://www.npmjs.com/package/qs) package\n- **request** query builder: `filter` and `or` methods can accept array of filter objects\n- **typeorm** changed visibility of all methods ([#226](https://github.com/nestjsx/crud/issues/226))\n- **typeorm** use `ILIKE` for PostgreSQL ([#212](https://github.com/nestjsx/crud/issues/212))\n\n### Bug Fixes\n\n- **crud** swagger: fixed response models ([#350](https://github.com/nestjsx/crud/issues/350))\n- **crud** swagger: fixed query params ([#196](https://github.com/nestjsx/crud/issues/196))\n- **crud** swagger: fixed renamed params ([#283](https://github.com/nestjsx/crud/issues/283))\n- **crud** swagger: fixed swagger method decoration on overridden methods\n- **crud** query parser: fixed parsing integers when it's a big int\n- **typeorm** fixed load embedded entities ([#138](https://github.com/nestjsx/crud/issues/138))\n- **typeorm** fixed left join issues ([#31](https://github.com/nestjsx/crud/issues/31), [#98](https://github.com/nestjsx/crud/issues/98))\n- **typeorm** fixed composite key joins ([#238](https://github.com/nestjsx/crud/issues/238))\n- **typeorm** fixed entity events ([#51](https://github.com/nestjsx/crud/issues/51))\n- **typeorm** all methods return entity instances ([#259](https://github.com/nestjsx/crud/issues/259))\n\n## [4.2.0] - 2019-07-26\n\n### Features\n\n- **crud** added support for older versions of `UUID` ([#186])\n\n### Bug Fixes\n\n- **crud** fixed `BulkDto` swagger description ([#159])\n- **crud** fixed `CrudRequestInterceptor` request parsing\n- **requests** added `@nestjsx/util` as a dependency ([#184])\n- **requests** fixed condition operators mapping ([#148])\n- **requests** fixed ISO date string validation ([#161])\n- **typeorm** fixed filtering and sorting by nested fields ([#105])\n- **typeorm** fixed `too many nested levels` exception ([#87])\n- **typeorm** fixed pagination `pageCount` ([#179])\n\n### Deps\n\n- **dev** updated deps\n\n## [4.1.0] - 2019-06-27\n\n### Features\n\n- **crud** added `PUT` request handling ([#107])\n- **requests** added creating request builder with params ([#131])\n- **requests** improved query params naming parsing ([#101])\n\n### Bug Fixes\n\n- **crud** set decorators after Swagger so metadata can be overwritten\n- **requests** added support for ISO-8610 date strings\n\n## [4.0.1] - 2019-06-21\n\n### Bug Fixes\n\n- **requests** fixed query parser to properly accept numbers and booleans ([#97])\n\n## [4.0.0] - 2019-06-12\n\n### BREAKING CHANGES\n\n- **crud:** changed `CrudOptions` ([docs](https://github.com/nestjsx/crud/wiki/Controllers#options))\n- **crud:** remove decorators: `@ParsedOptions`, `@ParsedParams`, `@ParsedQuery`. Add decorator `@ParsedRequest` instead.\n- **crud:** change interfaces\n- **services:** remove `RestfulOptions` from services\n- **services:** changed base abstract class\n\n### Features\n\n- **repo:** refactor to monorepository\n- **docs:** new [documentation](https://github.com/nestjsx/crud/wiki)\n- **packages:** totally refactor `@nestjsx/crud` to be service (ORM) agnostic\n- **packages:** add `@nestjsx/crud-typeorm` ([docs](https://github.com/nestjsx/crud/wiki/ServiceTypeorm))\n- **packages:** add `@nestjsx/crud-request` ([docs](https://github.com/nestjsx/crud/wiki/Requests#description), [#53])\n- **crud:** add global options ([docs](https://github.com/nestjsx/crud/wiki/Controllers#global-options), [#64])\n- **crud:** add eager relations option ([#54], [#67])\n\n### Bug Fixes\n\n- several fixes\n\n[4.6.2]: https://github.com/nestjsx/crud/compare/v4.6.1...v4.6.2\n[4.6.1]: https://github.com/nestjsx/crud/compare/v4.6.0...v4.6.1\n[4.6.0]: https://github.com/nestjsx/crud/compare/v4.5.0...v4.6.0\n[4.5.0]: https://github.com/nestjsx/crud/compare/v4.4.5...v4.5.0\n[4.4.5]: https://github.com/nestjsx/crud/compare/v4.4.4...v4.4.5\n[4.4.4]: https://github.com/nestjsx/crud/compare/v4.4.3...v4.4.4\n[4.4.3]: https://github.com/nestjsx/crud/compare/v4.4.2...v4.4.3\n[4.4.2]: https://github.com/nestjsx/crud/compare/v4.4.1...v4.4.2\n[4.4.1]: https://github.com/nestjsx/crud/compare/v4.4.0...v4.4.1\n[4.4.0]: https://github.com/nestjsx/crud/compare/v4.3.0...v4.4.0\n[4.3.0]: https://github.com/nestjsx/crud/compare/v4.2.0...v4.3.0\n[4.2.0]: https://github.com/nestjsx/crud/compare/v4.1.0...v4.2.0\n[4.1.0]: https://github.com/nestjsx/crud/compare/v4.0.1...v4.1.0\n[4.0.1]: https://github.com/nestjsx/crud/compare/v4.0.0...v4.0.1\n[4.0.0]: https://github.com/nestjsx/crud/compare/v.3.2.0...v4.0.0\n[#97]: https://github.com/nestjsx/crud/issues/97\n[#53]: https://github.com/nestjsx/crud/issues/53\n[#64]: https://github.com/nestjsx/crud/issues/64\n[#54]: https://github.com/nestjsx/crud/issues/54\n[#67]: https://github.com/nestjsx/crud/issues/67\n[#107]: https://github.com/nestjsx/crud/issues/107\n[#131]: https://github.com/nestjsx/crud/issues/131\n[#101]: https://github.com/nestjsx/crud/issues/101\n[#186]: https://github.com/nestjsx/crud/pull/186\n[#184]: https://github.com/nestjsx/crud/issues/184\n[#148]: https://github.com/nestjsx/crud/issues/148\n[#105]: https://github.com/nestjsx/crud/issues/105\n[#87]: https://github.com/nestjsx/crud/issues/87\n[#159]: https://github.com/nestjsx/crud/issues/159\n[#161]: https://github.com/nestjsx/crud/issues/161\n[#179]: https://github.com/nestjsx/crud/issues/179\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at mihon4ik@gmail.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "LICENSE",
    "content": "(The MIT License)\n\nCopyright (c) 2018-Present Michael Yali <mihon4ik@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://vshymanskyy.github.io/StandWithUkraine/)\n\n<div align=\"center\">\n  <h1>:point_right:<a href=\"https://github.com/nestjsx/crud/issues/784\">You can help NestJs CRUD\n  </a></h1>\n</div>\n\n<div align=\"center\">\n  <h1>CRUD</h1>\n</div>\n<div align=\"center\">\n  <strong>for RESTful APIs built with NestJs</strong>\n</div>\n\n<br />\n\n<div align=\"center\">\n  <a href=\"https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md\">\n    <img src=\"https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg\" alt=\"StandWithUkraine\" />\n  </a>\n  <a href=\"https://travis-ci.org/nestjsx/crud\">\n    <img src=\"https://github.com/nestjsx/crud/workflows/Tests/badge.svg\" alt=\"Build\" />\n  </a>\n  <a href=\"https://coveralls.io/github/nestjsx/crud?branch=master\">\n    <img src=\"https://coveralls.io/repos/github/nestjsx/crud/badge.svg\" alt=\"Coverage\" />\n  </a>\n  <a href=\"https://github.com/nestjsx/crud/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/nestjsx/crud.svg\" alt=\"License\" />\n  </a>\n  <a href=\"https://www.npmjs.com/package/@nestjsx/crud\">\n    <img src=\"https://img.shields.io/npm/v/@nestjsx/crud.svg\" alt=\"npm version\" />\n  </a>\n  <a href=\"https://www.npmjs.com/org/nestjsx\">\n    <img src=\"https://img.shields.io/npm/dm/@nestjsx/crud.svg\" alt=\"npm downloads\" />\n  </a>\n  <a href=\"http://makeapullrequest.com\">\n    <img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\" alt=\"PRs welcome\" />\n  </a>\n  <a href=\"https://github.com/marmelab/awesome-rest#nodejs\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-rest.svg?sanitize=true\" alt=\"Awesome REST\" />\n  </a>\n  <a href=\"#individuals\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/backers/badge.svg\" />\n  </a>\n  <a href=\"#organizations\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/sponsors/badge.svg\" />\n  </a> \n</div>\n\n<div align=\"center\">\n  <sub>Built with :purple_heart: by\n  <a href=\"https://twitter.com/MichaelYali\">@MichaelYali</a> and\n  <a href=\"https://github.com/nestjsx/crud/graphs/contributors\">\n    Contributors\n  </a>\n  <div align=\"center\">\n    :star2: :eyes: :zap: :boom:\n  </div>\n</div>\n\n<br />\n\nWe believe that everyone who's working with NestJs and building some RESTful services and especially some CRUD functionality will find `@nestjsx/crud` microframework very useful.\n\n## Features\n\n<img align=\"right\" src=\"img/crud-usage2.png\" alt=\"CRUD usage\" />\n\n- :electric_plug: Super easy to install and start using the full-featured controllers and services :point_right:\n\n- :octopus: DB and service agnostic extendable CRUD controllers\n\n- :mag_right: Reach query parsing with filtering, pagination, sorting, relations, nested relations, cache, etc.\n\n- :telescope: Framework agnostic package with query builder for a frontend usage\n\n- :space_invader: Query, path params and DTOs validation included\n\n- :clapper: Overriding controller methods with ease\n\n- :wrench: Tiny config (including globally)\n\n- :gift: Additional helper decorators\n\n- :pencil2: Swagger documentation\n\n## Packages\n\n- [**@nestjsx/crud**](https://www.npmjs.com/package/@nestjsx/crud) - core package which provides `@Crud()` decorator for endpoints generation, global configuration, validation, helper decorators ([docs](https://github.com/nestjsx/crud/wiki/Controllers#description))\n- [**@nestjsx/crud-request**](https://www.npmjs.com/package/@nestjsx/crud-request) - request builder/parser package which provides `RequestQueryBuilder` class for a frontend usage and `RequestQueryParser` that is being used internally for handling and validating query/path params on a backend side ([docs](https://github.com/nestjsx/crud/wiki/Requests#frontend-usage))\n- [**@nestjsx/crud-typeorm**](https://www.npmjs.com/package/@nestjsx/crud-typeorm) - TypeORM package which provides base `TypeOrmCrudService` with methods for CRUD database operations ([docs](https://github.com/nestjsx/crud/wiki/ServiceTypeorm))\n\n## Documentation\n\n- :dart: [General Information](https://github.com/nestjsx/crud/wiki#why)\n- :video_game: [CRUD Controllers](https://github.com/nestjsx/crud/wiki/Controllers#description)\n- :horse_racing: [CRUD ORM Services](https://github.com/nestjsx/crud/wiki/Services#description)\n- :trumpet: [Handling Requests](https://github.com/nestjsx/crud/wiki/Requests#description)\n\n## Support\n\nAny support is welcome. At least you can give us a star :star:\n\n## Contributors\n\n### Code Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CODE_OF_CONDUCT.md)].\n<a href=\"https://github.com/nestjsx/crud/graphs/contributors\"><img src=\"https://opencollective.com/nestjsx/contributors.svg?width=890&button=false\" /></a>\n\n### Financial Contributors\n\nBecome a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/nestjsx#backer)]\n\n#### Individuals\n\n<a href=\"https://opencollective.com/nestjsx#backers\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/backers.svg?width=890&button=false\"></a>\n\n#### Organizations\n\nSupport this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/nestjsx#sponsor)]\n\n<a href=\"https://opencollective.com/nestjsx/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/9/avatar.svg\"></a>\n\n## License\n\n[MIT](LICENSE)\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3'\n\nnetworks:\n  nestjsx_crud:\n\nservices:\n  postgres:\n    # TypeORM fails with Postgres v.12\n    image: postgres:11.5\n    ports:\n      - 5455:5432\n    environment:\n      POSTGRES_USER: root\n      POSTGRES_PASSWORD: root\n      POSTGRES_DB: nestjsx_crud\n    networks:\n      - nestjsx_crud\n\n  mysql:\n    platform: linux/x86_64\n    image: mysql:5.7\n    ports:\n      - 3316:3306\n    environment:\n      MYSQL_DATABASE: nestjsx_crud\n      MYSQL_USER: nestjsx_crud\n      MYSQL_PASSWORD: nestjsx_crud\n      MYSQL_ROOT_PASSWORD: nestjsx_crud\n\n  redis:\n    image: redis:alpine\n    ports:\n      - 6399:6379\n    command: redis-server\n    networks:\n      - nestjsx_crud\n"
  },
  {
    "path": "integration/crud-typeorm/app.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { APP_GUARD } from '@nestjs/core';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n\nimport { AuthGuard } from './auth.guard';\nimport { withCache } from './orm.config';\nimport { CompaniesModule } from './companies/companies.module';\nimport { ProjectsModule } from './projects/projects.module';\nimport { UsersModule } from './users/users.module';\nimport { DevicesModule } from './devices/devices.module';\nimport { NotesModule } from './notes/notes.module';\n\n@Module({\n  imports: [\n    TypeOrmModule.forRoot(withCache),\n    CompaniesModule,\n    ProjectsModule,\n    UsersModule,\n    DevicesModule,\n    NotesModule,\n  ],\n  providers: [\n    {\n      provide: APP_GUARD,\n      useClass: AuthGuard,\n    },\n  ],\n})\nexport class AppModule {}\n"
  },
  {
    "path": "integration/crud-typeorm/auth.guard.ts",
    "content": "import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';\n\nimport { UsersService } from './users';\nimport { USER_REQUEST_KEY } from './constants';\n\n@Injectable()\nexport class AuthGuard implements CanActivate {\n  constructor(private usersService: UsersService) {}\n\n  async canActivate(ctx: ExecutionContext): Promise<boolean> {\n    const req = ctx.switchToHttp().getRequest();\n    req[USER_REQUEST_KEY] = await this.usersService.findOne(1);\n\n    return true;\n  }\n}\n"
  },
  {
    "path": "integration/crud-typeorm/base-entity.ts",
    "content": "import { PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';\n\nexport class BaseEntity {\n  @PrimaryGeneratedColumn()\n  id?: number;\n\n  @CreateDateColumn({ nullable: true })\n  createdAt?: Date;\n\n  @UpdateDateColumn({ nullable: true })\n  updatedAt?: Date;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/companies/companies.controller.ts",
    "content": "import { Controller } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport { Crud } from '@nestjsx/crud';\n\nimport { Company } from './company.entity';\nimport { CompaniesService } from './companies.service';\nimport { serialize } from './responses';\n\n@Crud({\n  model: {\n    type: Company\n  },\n  serialize,\n  routes: {\n    deleteOneBase: {\n      returnDeleted: false,\n    },\n  },\n  query: {\n    alwaysPaginate: false,\n    softDelete: true,\n    allow: ['name'],\n    join: {\n      users: {\n        alias: 'companyUsers',\n        exclude: ['email'],\n        eager: true,\n      },\n      'users.projects': {\n        eager: true,\n        alias: 'usersProjects',\n        allow: ['name'],\n      },\n      'users.projects.company': {\n        eager: true,\n        alias: 'usersProjectsCompany',\n      },\n      projects: {\n        eager: true,\n        select: false,\n      },\n    },\n  },\n})\n@ApiTags('companies')\n@Controller('companies')\nexport class CompaniesController {\n  constructor(public service: CompaniesService) {}\n}\n"
  },
  {
    "path": "integration/crud-typeorm/companies/companies.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n\nimport { Company } from './company.entity';\nimport { CompaniesService } from './companies.service';\nimport { CompaniesController } from './companies.controller';\n\n@Module({\n  imports: [TypeOrmModule.forFeature([Company])],\n  providers: [CompaniesService],\n  exports: [CompaniesService],\n  controllers: [CompaniesController],\n})\nexport class CompaniesModule {}\n"
  },
  {
    "path": "integration/crud-typeorm/companies/companies.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { TypeOrmCrudService } from '@nestjsx/crud-typeorm';\n\nimport { Company } from './company.entity';\n\n@Injectable()\nexport class CompaniesService extends TypeOrmCrudService<Company> {\n  constructor(@InjectRepository(Company) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "integration/crud-typeorm/companies/company.entity.ts",
    "content": "import { CrudValidationGroups } from '@nestjsx/crud';\nimport { Entity, Column, OneToMany, PrimaryGeneratedColumn, DeleteDateColumn } from 'typeorm';\nimport {\n  IsOptional,\n  IsString,\n  MaxLength,\n  IsNotEmpty,\n  IsNumber,\n  IsEmpty,\n} from 'class-validator';\nimport { Type } from 'class-transformer';\n\nimport { BaseEntity } from '../base-entity';\nimport { User } from '../users/user.entity';\nimport { Project } from '../projects/project.entity';\n\nconst { CREATE, UPDATE } = CrudValidationGroups;\n\n@Entity('companies')\nexport class Company extends BaseEntity {\n  @IsOptional({ groups: [UPDATE] })\n  @IsEmpty({ groups: [CREATE] })\n  @IsNumber({}, { groups: [UPDATE] })\n  @PrimaryGeneratedColumn()\n  id?: number;\n\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @IsString({ always: true })\n  @MaxLength(100, { always: true })\n  @Column({ type: 'varchar', length: 100, nullable: false })\n  name: string;\n\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @IsString({ groups: [CREATE, UPDATE] })\n  @MaxLength(100, { groups: [CREATE, UPDATE] })\n  @Column({ type: 'varchar', length: 100, nullable: false, unique: true })\n  domain: string;\n\n  @IsOptional({ always: true })\n  @IsString({ always: true })\n  @Column({ type: 'text', nullable: true, default: null })\n  description: string;\n\n  @DeleteDateColumn({ nullable: true })\n  deletedAt?: Date;\n\n  /**\n   * Relations\n   */\n\n  @OneToMany((type) => User, (u) => u.company)\n  @Type((t) => User)\n  users: User[];\n\n  @OneToMany((type) => Project, (p) => p.company)\n  projects: Project[];\n}\n"
  },
  {
    "path": "integration/crud-typeorm/companies/index.ts",
    "content": "export * from './company.entity';\nexport * from './companies.service';\n"
  },
  {
    "path": "integration/crud-typeorm/companies/requests/create-company.dto.ts",
    "content": "import { ApiProperty } from '@nestjs/swagger';\nimport { IsString, MaxLength } from 'class-validator';\n\nexport class CreateCompanyDto {\n  @ApiProperty({ type: 'string' })\n  @IsString()\n  @MaxLength(100)\n  name: string;\n\n  @ApiProperty({ type: 'string' })\n  @IsString()\n  @MaxLength(100)\n  domain: string;\n\n  @ApiProperty({ type: 'string' })\n  @IsString()\n  @MaxLength(100)\n  description: string;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/companies/requests/index.ts",
    "content": "import { CreateCompanyDto } from './create-company.dto';\n\nexport const dto = {\n  create: CreateCompanyDto,\n};\n"
  },
  {
    "path": "integration/crud-typeorm/companies/responses/get-company-response.dto.ts",
    "content": "import { ApiProperty } from '@nestjs/swagger';\nimport { Exclude } from 'class-transformer';\n\nexport class GetCompanyResponseDto {\n  @ApiProperty({ type: 'number' })\n  id: string;\n\n  @ApiProperty({ type: 'string' })\n  name: string;\n\n  @ApiProperty({ type: 'string' })\n  domain: string;\n\n  @ApiProperty({ type: 'string' })\n  description: string;\n\n  @Exclude()\n  createdAt: any;\n\n  @Exclude()\n  updatedAt: any;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/companies/responses/index.ts",
    "content": "import { SerializeOptions } from '@nestjsx/crud';\nimport { GetCompanyResponseDto } from './get-company-response.dto';\n\nexport const serialize: SerializeOptions = {\n  get: GetCompanyResponseDto,\n};\n"
  },
  {
    "path": "integration/crud-typeorm/constants.ts",
    "content": "export const USER_REQUEST_KEY = 'user';\n"
  },
  {
    "path": "integration/crud-typeorm/devices/device.entity.ts",
    "content": "import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';\nimport { IsOptional, IsString, IsUUID } from 'class-validator';\nimport { CrudValidationGroups } from '@nestjsx/crud';\n\nconst { CREATE, UPDATE } = CrudValidationGroups;\n\n@Entity('devices')\nexport class Device {\n  @IsOptional({ always: true })\n  @IsUUID('4', { always: true })\n  @PrimaryGeneratedColumn('uuid')\n  deviceKey: string;\n\n  @IsOptional({ always: true })\n  @IsString({ always: true })\n  @Column({ type: 'text', nullable: true })\n  description?: string;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/devices/devices.controller.ts",
    "content": "import { Controller } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport { Crud } from '@nestjsx/crud';\n\nimport { Device } from './device.entity';\nimport { DevicesService } from './devices.service';\nimport { serialize } from './response';\n\n@Crud({\n  model: { type: Device },\n  serialize,\n  params: {\n    deviceKey: {\n      field: 'deviceKey',\n      type: 'uuid',\n      primary: true,\n    },\n  },\n  routes: {\n    deleteOneBase: {\n      returnDeleted: true,\n    },\n  },\n})\n@ApiTags('devices')\n@Controller('/devices')\nexport class DevicesController {\n  constructor(public service: DevicesService) {}\n}\n"
  },
  {
    "path": "integration/crud-typeorm/devices/devices.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n\nimport { Device } from './device.entity';\nimport { DevicesService } from './devices.service';\nimport { DevicesController } from './devices.controller';\n\n@Module({\n  imports: [TypeOrmModule.forFeature([Device])],\n  providers: [DevicesService],\n  exports: [DevicesService],\n  controllers: [DevicesController],\n})\nexport class DevicesModule {}\n"
  },
  {
    "path": "integration/crud-typeorm/devices/devices.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { TypeOrmCrudService } from '@nestjsx/crud-typeorm';\n\nimport { Device } from './device.entity';\n\n@Injectable()\nexport class DevicesService extends TypeOrmCrudService<Device> {\n  constructor(@InjectRepository(Device) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "integration/crud-typeorm/devices/index.ts",
    "content": "export * from './device.entity';\nexport * from './devices.service';\n"
  },
  {
    "path": "integration/crud-typeorm/devices/response/delete-device-response.dto.ts",
    "content": "import { ApiProperty } from '@nestjs/swagger';\nimport { Exclude } from 'class-transformer';\n\nexport class DeleteDeviceResponseDto {\n  @ApiProperty({ type: 'string' })\n  deviceKey: string;\n\n  @Exclude()\n  description?: string;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/devices/response/index.ts",
    "content": "import { SerializeOptions } from '@nestjsx/crud';\nimport { DeleteDeviceResponseDto } from './delete-device-response.dto';\n\nexport const serialize: SerializeOptions = {\n  delete: DeleteDeviceResponseDto,\n};\n"
  },
  {
    "path": "integration/crud-typeorm/main.ts",
    "content": "import { NestFactory } from '@nestjs/core';\nimport { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';\nimport { CrudConfigService } from '@nestjsx/crud';\nimport { USER_REQUEST_KEY } from './constants';\n\n// Important: load config before (!!!) you import AppModule\n// https://github.com/nestjsx/crud/wiki/Controllers#global-options\nCrudConfigService.load({\n  auth: {\n    property: USER_REQUEST_KEY,\n  },\n  routes: {\n    // exclude: ['createManyBase'],\n  },\n});\n\nimport { HttpExceptionFilter } from '../shared/https-exception.filter';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n\n  app.useGlobalFilters(new HttpExceptionFilter());\n\n  const options = new DocumentBuilder()\n    .setTitle('@nestjsx/crud-typeorm')\n    .setDescription('@nestjsx/crud-typeorm')\n    .setVersion('1.0')\n    .build();\n  const document = SwaggerModule.createDocument(app, options);\n  SwaggerModule.setup('docs', app, document);\n\n  await app.listen(process.env.PORT || 3000);\n}\n\nbootstrap();\n"
  },
  {
    "path": "integration/crud-typeorm/notes/index.ts",
    "content": "export * from './note.entity';\nexport * from './notes.service';\n"
  },
  {
    "path": "integration/crud-typeorm/notes/note.entity.ts",
    "content": "import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';\n\n@Entity('notes')\nexport class Note {\n  @PrimaryGeneratedColumn()\n  id: number;\n\n  @Column({ name: 'revision_id', nullable: false })\n  revisionId: number;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/notes/notes.controller.ts",
    "content": "import { Controller } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport { Crud } from '@nestjsx/crud';\n\nimport { Note } from './note.entity';\nimport { NotesService } from './notes.service';\nimport { dto } from './requests';\nimport { serialize } from './responses';\n\n@Crud({\n  model: { type: Note },\n  dto,\n  serialize,\n  query: {\n    alwaysPaginate: true,\n  },\n})\n@ApiTags('notes')\n@Controller('/notes')\nexport class NotesController {\n  constructor(public service: NotesService) {}\n}\n"
  },
  {
    "path": "integration/crud-typeorm/notes/notes.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n\nimport { Note } from './note.entity';\nimport { NotesService } from './notes.service';\nimport { NotesController } from './notes.controller';\n\n@Module({\n  imports: [TypeOrmModule.forFeature([Note])],\n  providers: [NotesService],\n  exports: [NotesService],\n  controllers: [NotesController],\n})\nexport class NotesModule {}\n"
  },
  {
    "path": "integration/crud-typeorm/notes/notes.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { TypeOrmCrudService } from '../../../packages/crud-typeorm/src';\n\nimport { Note } from './note.entity';\n\n@Injectable()\nexport class NotesService extends TypeOrmCrudService<Note> {\n  constructor(@InjectRepository(Note) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "integration/crud-typeorm/notes/requests/create-note.dto.ts",
    "content": "import { ApiProperty } from '@nestjs/swagger';\nimport { IsNumber } from 'class-validator';\n\nexport class CreateNoteDto {\n  @ApiProperty({ type: 'number' })\n  @IsNumber()\n  revisionId: string;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/notes/requests/index.ts",
    "content": "import { CreateNoteDto } from './create-note.dto';\n\nexport const dto = {\n  create: CreateNoteDto,\n};\n"
  },
  {
    "path": "integration/crud-typeorm/notes/responses/get-note-response.dto.ts",
    "content": "import { ApiProperty } from '@nestjs/swagger';\nimport { IsNumber } from 'class-validator';\n\nexport class GetNoteResponseDto {\n  @ApiProperty({ type: 'number' })\n  @IsNumber()\n  id: string;\n\n  @ApiProperty({ type: 'number' })\n  @IsNumber()\n  revisionId: string;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/notes/responses/index.ts",
    "content": "import { GetNoteResponseDto } from './get-note-response.dto';\n\nexport const serialize = {\n  get: GetNoteResponseDto,\n};\n"
  },
  {
    "path": "integration/crud-typeorm/orm.config.ts",
    "content": "import { join } from 'path';\nimport { TypeOrmModuleOptions } from '@nestjs/typeorm';\nimport { isNil } from '@nestjsx/util';\n\nconst type = (process.env.TYPEORM_CONNECTION as any) || 'postgres';\n\nexport const withCache: TypeOrmModuleOptions = {\n  type,\n  host: '127.0.0.1',\n  port: type === 'postgres' ? 5455 : 3316,\n  username: type === 'mysql' ? 'nestjsx_crud' : 'root',\n  password: type === 'mysql' ? 'nestjsx_crud' : 'root',\n  database: 'nestjsx_crud',\n  synchronize: false,\n  logging: !isNil(process.env.TYPEORM_LOGGING) ? !!parseInt(process.env.TYPEORM_LOGGING, 10) : true,\n  entities: [join(__dirname, './**/*.entity{.ts,.js}')],\n};\n"
  },
  {
    "path": "integration/crud-typeorm/orm.mysql.ts",
    "content": "import { DataSource } from 'typeorm';\n\nexports.default = new DataSource({\n  type: 'mysql',\n  host: '127.0.0.1',\n  port: 3316,\n  username: 'nestjsx_crud',\n  password: 'nestjsx_crud',\n  database: 'nestjsx_crud',\n  entities: ['./**/*.entity.ts'],\n  migrationsTableName: 'orm_migrations',\n  migrations: ['./seeds.ts'],\n});\n"
  },
  {
    "path": "integration/crud-typeorm/orm.postgres.ts",
    "content": "import { DataSource } from 'typeorm';\n\nexports.default = new DataSource({\n  type: 'postgres',\n  host: '127.0.0.1',\n  port: 5455,\n  username: 'root',\n  password: 'root',\n  database: 'nestjsx_crud',\n  entities: ['./**/*.entity.ts'],\n  migrationsTableName: 'orm_migrations',\n  migrations: ['./seeds.ts'],\n});\n"
  },
  {
    "path": "integration/crud-typeorm/orm.yaml",
    "content": "default:\n  type: postgres\n  host: 127.0.0.1\n  port: 5455\n  username: root\n  password: root\n  database: nestjsx_crud\n  entities:\n    - ./**/*.entity.ts\n  migrationsTableName: orm_migrations\n  migrations:\n    - ./seeds.ts\nmysql:\n  type: mysql\n  host: 127.0.0.1\n  port: 3316\n  username: nestjsx_crud\n  password: nestjsx_crud\n  database: nestjsx_crud\n  entities:\n    - ./**/*.entity.ts\n  migrationsTableName: orm_migrations\n  migrations:\n    - ./seeds.ts\n"
  },
  {
    "path": "integration/crud-typeorm/projects/index.ts",
    "content": "export * from './project.entity';\nexport * from './user-project.entity';\nexport * from './projects.service';\n"
  },
  {
    "path": "integration/crud-typeorm/projects/my-projects.controller.ts",
    "content": "import { Controller } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport { Crud, CrudAuth } from '@nestjsx/crud';\n\nimport { User } from '../users/user.entity';\nimport { UserProject } from './user-project.entity';\nimport { UserProjectsService } from './user-projects.service';\n\n@Crud({\n  model: {\n    type: UserProject,\n  },\n  params: {\n    projectId: {\n      field: 'projectId',\n      type: 'number',\n      primary: true,\n    },\n  },\n  query: {\n    join: {\n      project: {\n        eager: true,\n      },\n    },\n  },\n})\n@CrudAuth({\n  filter: (user: User) => ({\n    userId: user.id,\n  }),\n  persist: (user: User) => ({\n    userId: user.id,\n  }),\n})\n@ApiTags('my-projects')\n@Controller('my-projects')\nexport class MyProjectsController {\n  constructor(public service: UserProjectsService) {}\n}\n"
  },
  {
    "path": "integration/crud-typeorm/projects/project.entity.ts",
    "content": "import { Entity, Column, ManyToOne, ManyToMany, JoinTable, OneToMany } from 'typeorm';\nimport {\n  IsOptional,\n  IsString,\n  IsNumber,\n  MaxLength,\n  IsDefined,\n  IsBoolean,\n} from 'class-validator';\nimport { CrudValidationGroups } from '@nestjsx/crud';\n\nimport { BaseEntity } from '../base-entity';\nimport { Company } from '../companies/company.entity';\nimport { User } from '../users/user.entity';\nimport { UserProject } from './user-project.entity';\n\nconst { CREATE, UPDATE } = CrudValidationGroups;\n\n@Entity('projects')\nexport class Project extends BaseEntity {\n  @IsOptional({ groups: [UPDATE] })\n  @IsDefined({ groups: [CREATE] })\n  @IsString({ always: true })\n  @MaxLength(100, { always: true })\n  @Column({ type: 'varchar', length: 100, nullable: false, unique: true })\n  name?: string;\n\n  @IsOptional({ always: true })\n  @Column({ type: 'text', nullable: true })\n  description?: string;\n\n  @IsOptional({ always: true })\n  @IsBoolean({ always: true })\n  @Column({ type: 'boolean', default: true })\n  isActive?: boolean;\n\n  @IsOptional({ always: true })\n  @IsNumber({}, { always: true })\n  @Column({ nullable: false })\n  companyId?: number;\n\n  /**\n   * Relations\n   */\n\n  @ManyToOne((type) => Company, (c) => c.projects)\n  company?: Company;\n\n  @ManyToMany((type) => User, (u) => u.projects, { cascade: true })\n  @JoinTable({\n    name: 'user_projects',\n    joinColumn: {\n      name: 'projectId',\n      referencedColumnName: 'id',\n    },\n    inverseJoinColumn: {\n      name: 'userId',\n      referencedColumnName: 'id',\n    },\n  })\n  users?: User[];\n\n  @OneToMany((type) => UserProject, (el) => el.project, {\n    persistence: false,\n    onDelete: 'CASCADE',\n  })\n  userProjects!: UserProject[];\n}\n"
  },
  {
    "path": "integration/crud-typeorm/projects/projects.controller.ts",
    "content": "import { Controller } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport { Crud } from '@nestjsx/crud';\n\nimport { Project } from './project.entity';\nimport { ProjectsService } from './projects.service';\n\n@Crud({\n  model: {\n    type: Project,\n  },\n  params: {\n    companyId: {\n      field: 'companyId',\n      type: 'number',\n    },\n    id: {\n      field: 'id',\n      type: 'number',\n      primary: true,\n    },\n  },\n  query: {\n    join: {\n      users: {},\n    },\n  },\n})\n@ApiTags('projects')\n@Controller('/companies/:companyId/projects')\nexport class ProjectsController {\n  constructor(public service: ProjectsService) {}\n}\n"
  },
  {
    "path": "integration/crud-typeorm/projects/projects.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n\nimport { Project } from './project.entity';\nimport { UserProject } from './user-project.entity';\nimport { ProjectsService } from './projects.service';\nimport { UserProjectsService } from './user-projects.service';\nimport { ProjectsController } from './projects.controller';\nimport { MyProjectsController } from './my-projects.controller';\n\n@Module({\n  imports: [TypeOrmModule.forFeature([Project, UserProject])],\n  providers: [ProjectsService, UserProjectsService],\n  exports: [ProjectsService, UserProjectsService],\n  controllers: [ProjectsController, MyProjectsController],\n})\nexport class ProjectsModule {}\n"
  },
  {
    "path": "integration/crud-typeorm/projects/projects.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { TypeOrmCrudService } from '@nestjsx/crud-typeorm';\n\nimport { Project } from './project.entity';\n\n@Injectable()\nexport class ProjectsService extends TypeOrmCrudService<Project> {\n  constructor(@InjectRepository(Project) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "integration/crud-typeorm/projects/user-project.entity.ts",
    "content": "import { Entity, Column, ManyToOne, PrimaryColumn } from 'typeorm';\n\nimport { User } from '../users/user.entity';\nimport { Project } from './project.entity';\n\n@Entity('user_projects')\nexport class UserProject {\n  @PrimaryColumn()\n  public projectId!: number;\n\n  @PrimaryColumn()\n  public userId!: number;\n\n  @Column({ nullable: true })\n  public review!: string;\n\n  @ManyToOne(() => Project, (el) => el.userProjects, {\n    persistence: false,\n    onDelete: 'CASCADE',\n  })\n  public project: Project;\n\n  @ManyToOne(() => User, (el) => el.userProjects, {\n    persistence: false,\n  })\n  public user: User;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/projects/user-projects.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { TypeOrmCrudService } from '@nestjsx/crud-typeorm';\n\nimport { UserProject } from './user-project.entity';\n\n@Injectable()\nexport class UserProjectsService extends TypeOrmCrudService<UserProject> {\n  constructor(@InjectRepository(UserProject) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "integration/crud-typeorm/seeds.ts",
    "content": "import { ClassType } from '@nestjsx/util';\nimport { plainToClass } from 'class-transformer';\nimport { MigrationInterface, Repository, QueryRunner } from 'typeorm';\nimport { Company } from './companies';\nimport { Project, UserProject } from './projects';\nimport { Name, User } from './users';\nimport { License, UserLicense } from './users-licenses';\nimport { UserProfile } from './users-profiles';\nimport { Note } from './notes';\n\nexport class Seeds1544303473346 implements MigrationInterface {\n  private save<T>(repo: Repository<T>, data: Partial<T>[]): Promise<T[]> {\n    return repo.save(\n      data.map((partial: Partial<T>) =>\n        plainToClass<any, any>(repo.target as ClassType<T>, partial, {\n          ignoreDecorators: true,\n        }),\n      ),\n    );\n  }\n\n  public async up(queryRunner: QueryRunner): Promise<any> {\n    const { connection } = queryRunner;\n\n    const companiesRepo = connection.getRepository(Company);\n    const projectsRepo = connection.getRepository(Project);\n    const usersProfilesRepo = connection.getRepository(UserProfile);\n    const usersRepo = connection.getRepository(User);\n    const licensesRepo = connection.getRepository(License);\n    const usersLincesesRepo = connection.getRepository(UserLicense);\n    const usersProjectsRepo = connection.getRepository(UserProject);\n    const notesRepo = connection.getRepository(Note);\n\n    // companies\n    await this.save(companiesRepo, [\n      { name: 'Name1', domain: 'Domain1' },\n      { name: 'Name2', domain: 'Domain2' },\n      { name: 'Name3', domain: 'Domain3' },\n      { name: 'Name4', domain: 'Domain4' },\n      { name: 'Name5', domain: 'Domain5' },\n      { name: 'Name6', domain: 'Domain6' },\n      { name: 'Name7', domain: 'Domain7' },\n      { name: 'Name8', domain: 'Domain8' },\n      { name: 'Name9', domain: 'Domain9', deletedAt: new Date() },\n      { name: 'Name10', domain: 'Domain10' },\n    ]);\n\n    // projects\n    await this.save(projectsRepo, [\n      { name: 'Project1', description: 'description1', isActive: true, companyId: 1 },\n      { name: 'Project2', description: 'description2', isActive: true, companyId: 1 },\n      { name: 'Project3', description: 'description3', isActive: true, companyId: 2 },\n      { name: 'Project4', description: 'description4', isActive: true, companyId: 2 },\n      { name: 'Project5', description: 'description5', isActive: true, companyId: 3 },\n      { name: 'Project6', description: 'description6', isActive: true, companyId: 3 },\n      { name: 'Project7', description: 'description7', isActive: true, companyId: 4 },\n      { name: 'Project8', description: 'description8', isActive: true, companyId: 4 },\n      { name: 'Project9', description: 'description9', isActive: true, companyId: 5 },\n      { name: 'Project10', description: 'description10', isActive: true, companyId: 5 },\n      { name: 'Project11', description: 'description11', isActive: false, companyId: 6 },\n      { name: 'Project12', description: 'description12', isActive: false, companyId: 6 },\n      { name: 'Project13', description: 'description13', isActive: false, companyId: 7 },\n      { name: 'Project14', description: 'description14', isActive: false, companyId: 7 },\n      { name: 'Project15', description: 'description15', isActive: false, companyId: 8 },\n      { name: 'Project16', description: 'description16', isActive: false, companyId: 8 },\n      { name: 'Project17', description: 'description17', isActive: false, companyId: 9 },\n      { name: 'Project18', description: 'description18', isActive: false, companyId: 9 },\n      { name: 'Project19', description: 'description19', isActive: false, companyId: 10 },\n      { name: 'Project20', description: 'description20', isActive: false, companyId: 10 },\n    ]);\n\n    // user-profiles\n    await this.save(usersProfilesRepo, [\n      { name: 'User1' },\n      { name: 'User2' },\n      { name: 'User3' },\n      { name: 'User4' },\n      { name: 'User5' },\n      { name: 'User6' },\n      { name: 'User7' },\n      { name: 'User8' },\n      { name: 'User9' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User1' },\n      { name: 'User2' },\n    ]);\n\n    // users\n    const name: Name = { first: null, last: null };\n    const name1: Name = { first: 'firstname1', last: 'lastname1' };\n    await this.save(usersRepo, [\n      { email: '1@email.com', isActive: true, companyId: 1, profileId: 1, name: name1 },\n      { email: '2@email.com', isActive: true, companyId: 1, profileId: 2, name },\n      { email: '3@email.com', isActive: true, companyId: 1, profileId: 3, name },\n      { email: '4@email.com', isActive: true, companyId: 1, profileId: 4, name },\n      { email: '5@email.com', isActive: true, companyId: 1, profileId: 5, name },\n      { email: '6@email.com', isActive: true, companyId: 1, profileId: 6, name },\n      { email: '7@email.com', isActive: false, companyId: 1, profileId: 7, name },\n      { email: '8@email.com', isActive: false, companyId: 1, profileId: 8, name },\n      { email: '9@email.com', isActive: false, companyId: 1, profileId: 9, name },\n      { email: '10@email.com', isActive: true, companyId: 1, profileId: 10, name },\n      { email: '11@email.com', isActive: true, companyId: 2, profileId: 11, name },\n      { email: '12@email.com', isActive: true, companyId: 2, profileId: 12, name },\n      { email: '13@email.com', isActive: true, companyId: 2, profileId: 13, name },\n      { email: '14@email.com', isActive: true, companyId: 2, profileId: 14, name },\n      { email: '15@email.com', isActive: true, companyId: 2, profileId: 15, name },\n      { email: '16@email.com', isActive: true, companyId: 2, profileId: 16, name },\n      { email: '17@email.com', isActive: false, companyId: 2, profileId: 17, name },\n      { email: '18@email.com', isActive: false, companyId: 2, profileId: 18, name },\n      { email: '19@email.com', isActive: false, companyId: 2, profileId: 19, name },\n      { email: '20@email.com', isActive: false, companyId: 2, profileId: 20, name },\n      { email: '21@email.com', isActive: false, companyId: 2, profileId: null, name },\n    ]);\n\n    // licenses\n    await this.save(licensesRepo, [\n      { name: 'License1' },\n      { name: 'License2' },\n      { name: 'License3' },\n      { name: 'License4' },\n      { name: 'License5' },\n    ]);\n\n    // user-licenses\n    await this.save(usersLincesesRepo, [\n      { userId: 1, licenseId: 1, yearsActive: 3 },\n      { userId: 1, licenseId: 2, yearsActive: 5 },\n      { userId: 1, licenseId: 4, yearsActive: 7 },\n      { userId: 2, licenseId: 5, yearsActive: 1 },\n    ]);\n\n    // user-projects\n    await this.save(usersProjectsRepo, [\n      { projectId: 1, userId: 1, review: 'User project 1 1' },\n      { projectId: 1, userId: 2, review: 'User project 1 2' },\n      { projectId: 2, userId: 2, review: 'User project 2 2' },\n      { projectId: 3, userId: 3, review: 'User project 3 3' },\n    ]);\n\n    // notes\n    await this.save(notesRepo, [\n      { revisionId: 1 },\n      { revisionId: 1 },\n      { revisionId: 2 },\n      { revisionId: 2 },\n      { revisionId: 3 },\n      { revisionId: 3 },\n    ]);\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<any> {}\n}\n"
  },
  {
    "path": "integration/crud-typeorm/users/index.ts",
    "content": "export * from './user.entity';\nexport * from './users.service';\n"
  },
  {
    "path": "integration/crud-typeorm/users/me.controller.ts",
    "content": "import { Controller } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport { Crud, CrudAuth } from '@nestjsx/crud';\n\nimport { User } from './user.entity';\nimport { UsersService } from './users.service';\n\n@Crud({\n  model: {\n    type: User,\n  },\n  routes: {\n    only: ['getOneBase', 'updateOneBase'],\n  },\n  params: {\n    id: {\n      primary: true,\n      disabled: true,\n    },\n  },\n  query: {\n    join: {\n      company: {\n        eager: true,\n      },\n      profile: {\n        eager: true,\n      },\n    },\n  },\n})\n@CrudAuth({\n  filter: (user: User) => ({\n    id: user.id,\n  }),\n})\n@ApiTags('me')\n@Controller('me')\nexport class MeController {\n  constructor(public service: UsersService) {}\n}\n"
  },
  {
    "path": "integration/crud-typeorm/users/user.entity.ts",
    "content": "import {\n  Entity,\n  Column,\n  JoinColumn,\n  OneToOne,\n  OneToMany,\n  ManyToOne,\n  ManyToMany,\n  DeleteDateColumn,\n} from 'typeorm';\nimport {\n  IsOptional,\n  IsString,\n  MaxLength,\n  IsNotEmpty,\n  IsEmail,\n  IsBoolean,\n  ValidateNested,\n} from 'class-validator';\nimport { Type } from 'class-transformer';\nimport { CrudValidationGroups } from '@nestjsx/crud';\n\nimport { BaseEntity } from '../base-entity';\nimport { UserProfile } from '../users-profiles/user-profile.entity';\nimport { UserLicense } from '../users-licenses/user-license.entity';\nimport { Company } from '../companies/company.entity';\nimport { Project } from '../projects/project.entity';\nimport { UserProject } from '../projects/user-project.entity';\n\nconst { CREATE, UPDATE } = CrudValidationGroups;\n\nexport class Name {\n  @IsString({ always: true })\n  @Column({ nullable: true })\n  first: string;\n\n  @IsString({ always: true })\n  @Column({ nullable: true })\n  last: string;\n}\n\n// tslint:disable-next-line:max-classes-per-file\n@Entity('users')\nexport class User extends BaseEntity {\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @IsString({ always: true })\n  @MaxLength(255, { always: true })\n  @IsEmail({ require_tld: false }, { always: true })\n  @Column({ type: 'varchar', length: 255, nullable: false, unique: true })\n  email: string;\n\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @IsBoolean({ always: true })\n  @Column({ type: 'boolean', default: true })\n  isActive: boolean;\n\n  @Type((t) => Name)\n  @Column((type) => Name)\n  name: Name;\n\n  @Column({ nullable: true })\n  profileId?: number;\n\n  @Column({ nullable: false })\n  companyId?: number;\n\n  @DeleteDateColumn({ nullable: true })\n  deletedAt?: Date;\n\n  /**\n   * Relations\n   */\n\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @ValidateNested({ always: true })\n  @Type((t) => UserProfile)\n  @OneToOne((type) => UserProfile, (p) => p.user, { cascade: true })\n  @JoinColumn()\n  profile?: UserProfile;\n\n  @ManyToOne((type) => Company, (c) => c.users)\n  company?: Company;\n\n  @ManyToMany((type) => Project, (c) => c.users)\n  projects?: Project[];\n\n  @OneToMany((type) => UserProject, (el) => el.user, {\n    persistence: false,\n    onDelete: 'CASCADE',\n  })\n  userProjects?: UserProject[];\n\n  @OneToMany((type) => UserLicense, (ul) => ul.user)\n  @Type((t) => UserLicense)\n  @JoinColumn()\n  userLicenses?: UserLicense[];\n}\n"
  },
  {
    "path": "integration/crud-typeorm/users/users.controller.ts",
    "content": "import { Controller } from '@nestjs/common';\nimport { ApiTags } from '@nestjs/swagger';\nimport {\n  Crud,\n  CrudController,\n  CrudRequest,\n  ParsedRequest,\n  Override,\n} from '@nestjsx/crud';\n\nimport { User } from './user.entity';\nimport { UsersService } from './users.service';\n\n@Crud({\n  model: {\n    type: User,\n  },\n  params: {\n    companyId: {\n      field: 'companyId',\n      type: 'number',\n    },\n    id: {\n      field: 'id',\n      type: 'number',\n      primary: true,\n    },\n  },\n  query: {\n    softDelete: true,\n    join: {\n      company: {\n        exclude: ['description'],\n      },\n      'company.projects': {\n        alias: 'pr',\n        exclude: ['description'],\n      },\n      profile: {\n        eager: true,\n        exclude: ['updatedAt'],\n      },\n    },\n  },\n})\n@ApiTags('users')\n@Controller('/companies/:companyId/users')\nexport class UsersController implements CrudController<User> {\n  constructor(public service: UsersService) {}\n\n  get base(): CrudController<User> {\n    return this;\n  }\n\n  @Override('getManyBase')\n  getAll(@ParsedRequest() req: CrudRequest) {\n    return this.base.getManyBase(req);\n  }\n}\n"
  },
  {
    "path": "integration/crud-typeorm/users/users.module.ts",
    "content": "import { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n\nimport { User } from './user.entity';\nimport { UserProfile } from '../users-profiles/user-profile.entity';\nimport { UsersService } from './users.service';\nimport { UsersController } from './users.controller';\nimport { MeController } from './me.controller';\n\n@Module({\n  imports: [TypeOrmModule.forFeature([User, UserProfile])],\n  providers: [UsersService],\n  exports: [UsersService],\n  controllers: [UsersController, MeController],\n})\nexport class UsersModule {}\n"
  },
  {
    "path": "integration/crud-typeorm/users/users.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { TypeOrmCrudService } from '@nestjsx/crud-typeorm';\n\nimport { User } from './user.entity';\n\n@Injectable()\nexport class UsersService extends TypeOrmCrudService<User> {\n  constructor(@InjectRepository(User) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "integration/crud-typeorm/users-licenses/index.ts",
    "content": "export * from './license.entity';\nexport * from './user-license.entity';\n"
  },
  {
    "path": "integration/crud-typeorm/users-licenses/license.entity.ts",
    "content": "import { Column, Entity } from 'typeorm';\nimport { BaseEntity } from '../base-entity';\nimport { IsOptional, IsString, MaxLength } from 'class-validator';\n\n@Entity('licenses')\nexport class License extends BaseEntity {\n  @IsOptional({ always: true })\n  @IsString({ always: true })\n  @MaxLength(32, { always: true })\n  @Column({ type: 'varchar', length: 32, nullable: true, default: null })\n  name: string;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/users-licenses/user-license.entity.ts",
    "content": "import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm';\nimport { User } from '../users/user.entity';\nimport { Type } from 'class-transformer';\nimport { License } from './license.entity';\n\n@Entity('user_licenses')\nexport class UserLicense {\n  @PrimaryColumn()\n  userId: number;\n\n  @PrimaryColumn()\n  licenseId: number;\n\n  @ManyToOne((type) => User)\n  @Type((t) => User)\n  user: User;\n\n  @ManyToOne((type) => License)\n  @Type((t) => License)\n  license: License;\n\n  @Column()\n  yearsActive: number;\n}\n"
  },
  {
    "path": "integration/crud-typeorm/users-profiles/index.ts",
    "content": "export * from './user-profile.entity';\n"
  },
  {
    "path": "integration/crud-typeorm/users-profiles/user-profile.entity.ts",
    "content": "import { Entity, Column, OneToOne, DeleteDateColumn } from 'typeorm';\nimport { IsOptional, IsString, MaxLength } from 'class-validator';\n\nimport { BaseEntity } from '../base-entity';\nimport { User } from '../users/user.entity';\n\n@Entity('user_profiles')\nexport class UserProfile extends BaseEntity {\n  @IsOptional({ always: true })\n  @IsString({ always: true })\n  @MaxLength(32, { always: true })\n  @Column({ type: 'varchar', length: 32, nullable: true, default: null })\n  name: string;\n\n  @DeleteDateColumn({ nullable: true })\n  deletedAt?: Date;\n\n  /**\n   * Relations\n   */\n\n  @OneToOne((type) => User, (u) => u.profile)\n  user?: User;\n}\n"
  },
  {
    "path": "integration/shared/https-exception.filter.ts",
    "content": "import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';\nimport { HttpException, InternalServerErrorException } from '@nestjs/common';\n\n@Catch()\nexport class HttpExceptionFilter implements ExceptionFilter {\n  catch(exception: HttpException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse();\n    const { status, json } = this.prepareException(exception);\n\n    response.status(status).send(json);\n  }\n\n  prepareException(exc: any): { status: number; json: object } {\n    if (process.env.NODE_ENV !== 'test') {\n      console.log(exc);\n    }\n\n    const error =\n      exc instanceof HttpException ? exc : new InternalServerErrorException(exc.message);\n    const status = error.getStatus();\n    const response = error.getResponse();\n    const json = typeof response === 'string' ? { error: response } : response;\n\n    return { status, json };\n  }\n}\n"
  },
  {
    "path": "jest.config.js",
    "content": "/* eslint-disable @typescript-eslint/no-var-requires */\n\nconst tsconfig = require('tsconfig-extends');\nconst { pathsToModuleNameMapper } = require('ts-jest/utils');\nconst compilerOptions = tsconfig.load_file_sync('./tsconfig.jest.json', __dirname);\n\nmodule.exports = {\n  testEnvironment: 'node',\n  setupFilesAfterEnv: ['jest-extended/all'],\n  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, {\n    prefix: '<rootDir>/packages/',\n  }),\n  moduleFileExtensions: ['ts', 'js'],\n  testRegex: '\\\\.spec.ts$',\n  rootDir: '.',\n  transform: {\n    '^.+\\\\.ts$': 'ts-jest',\n  },\n  globals: {\n    'ts-jest': {\n      tsconfig: 'tsconfig.jest.json',\n    },\n  },\n  coverageReporters: ['json', 'lcov', 'text-summary'],\n  coverageDirectory: 'coverage',\n  collectCoverageFrom: [\n    'packages/**/*.ts',\n    '!packages/**/*.d.ts',\n    '!packages/**/index.ts',\n    '!packages/**/*.interface.ts',\n    '!**/node_modules/**',\n    '!**/__stubs__/**',\n    '!**/__fixture__/**',\n    '!integration/*',\n  ],\n};\n"
  },
  {
    "path": "lerna.json",
    "content": "{\n  \"packages\": [\n    \"packages/*\"\n  ],\n  \"npmClient\": \"yarn\",\n  \"useWorkspaces\": true,\n  \"version\": \"5.0.0-alpha.3\",\n  \"command\": {\n    \"publish\": {\n      \"message\": \"chore: release\"\n    }\n  }\n}\n"
  },
  {
    "path": "mrepo.json",
    "content": "{\n  \"workspace\": {\n    \"name\": \"packages\",\n    \"scope\": \"nestjsx\",\n    \"registry\": \"npm\",\n    \"packages\": [\n      {\n        \"name\": \"util\"\n      },\n      {\n        \"name\": \"crud-request\"\n      },\n      {\n        \"name\": \"crud-typeorm\"\n      },\n      {\n        \"name\": \"crud\"\n      }\n    ]\n  },\n  \"packageGenerator\": {\n    \"subGenerators\": [],\n    \"defaultOptions\": {\n      \"access\": \"public\",\n      \"license\": \"MIT\",\n      \"authorName\": \"Michael Yali\",\n      \"authorEmail\": \"mihon4ik@gmail.com\",\n      \"updateTsconfig\": true,\n      \"subGenerators\": [],\n      \"dependencies\": []\n    }\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@nestjsx/crud\",\n  \"version\": \"4.0.0\",\n  \"description\": \"Nest CRUD for RESTful APIs\",\n  \"license\": \"MIT\",\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"private\": true,\n  \"scripts\": {\n    \"boot\": \"npx lerna bootstrap\",\n    \"rebuild\": \"yarn clean && yarn build\",\n    \"build\": \"npx mrepo build\",\n    \"clean\": \"npx mrepo clean && npx rimraf ./.mrepo\",\n    \"test\": \"npx mrepo test\",\n    \"test:coverage\": \"yarn test:all --coverage\",\n    \"test:coveralls\": \"yarn test:coverage --coverageReporters=text-lcov | coveralls\",\n    \"test:all\": \"yarn test:mysql && yarn test:postgres\",\n    \"test:postgres\": \"yarn db:prepare:typeorm:postgres && yarn test\",\n    \"test:mysql\": \"yarn db:prepare:typeorm:mysql && TYPEORM_CONNECTION=mysql yarn test\",\n    \"start:typeorm\": \"npx nodemon -w ./integration/crud-typeorm -e ts node_modules/ts-node/dist/bin.js integration/crud-typeorm/main.ts\",\n    \"db:cli:typeorm\": \"cd ./integration/crud-typeorm && npx ts-node -r tsconfig-paths/register ../../node_modules/typeorm/cli.js\",\n    \"db:sync:typeorm\": \"yarn db:cli:typeorm schema:sync\",\n    \"db:drop:typeorm\": \"yarn db:cli:typeorm schema:drop\",\n    \"db:seeds:typeorm\": \"yarn db:cli:typeorm migration:run\",\n    \"db:prepare:typeorm:postgres\": \"yarn db:drop:typeorm -d=orm.postgres.ts && yarn db:sync:typeorm -d=orm.postgres.ts && yarn db:seeds:typeorm -d=orm.postgres.ts\",\n    \"db:prepare:typeorm:mysql\": \"yarn db:drop:typeorm -d=orm.mysql.ts && yarn db:sync:typeorm -d=orm.mysql.ts && yarn db:seeds:typeorm -d=orm.mysql.ts\",\n    \"format\": \"npx pretty-quick --pattern \\\"packages/**/!(*.d).ts\\\"\",\n    \"lint\": \"npx eslint \\\"packages/**/!(*.d).ts\\\" --fix\",\n    \"commit\": \"npx git-cz\",\n    \"postinstall\": \"npx opencollective\",\n    \"prepare\": \"npx husky install\",\n    \"release\": \"mrepo release\",\n    \"release:alpha\": \"yarn release prerelease --preid alpha --dist-tag alpha\",\n    \"release:beta\": \"yarn release prerelease --preid beta --dist-tag beta\"\n  },\n  \"config\": {\n    \"commitizen\": {\n      \"path\": \"node_modules/cz-conventional-changelog\"\n    }\n  },\n  \"collective\": {\n    \"type\": \"opencollective\",\n    \"url\": \"https://opencollective.com/nestjsx\",\n    \"donation\": {\n      \"text\": \"Become a partner:\"\n    }\n  },\n  \"dependencies\": {\n    \"@nestjs/common\": \"^9.0.0\",\n    \"@nestjs/core\": \"^9.0.0\",\n    \"@nestjs/platform-express\": \"^9.0.0\",\n    \"@nestjs/swagger\": \"5.2.1\",\n    \"@nestjs/testing\": \"^9.0.0\",\n    \"@nestjs/typeorm\": \"^9.0.0\",\n    \"@zmotivat0r/mrepo\": \"0.8.1\",\n    \"class-transformer\": \"0.3.2\",\n    \"class-validator\": \"0.13.2\",\n    \"mysql\": \"2.18.1\",\n    \"pg\": \"8.7.3\",\n    \"pluralize\": \"8.0.0\",\n    \"qs\": \"6.10.3\",\n    \"redis\": \"4.0.4\",\n    \"reflect-metadata\": \"0.1.13\",\n    \"rxjs\": \"7.5.5\",\n    \"swagger-ui-express\": \"4.3.0\",\n    \"typeorm\": \"^0.3.0\"\n  },\n  \"devDependencies\": {\n    \"@nuxtjs/opencollective\": \"0.2.2\",\n    \"@types/jest\": \"27.4.1\",\n    \"@types/node\": \"17.0.23\",\n    \"@types/qs\": \"6.9.7\",\n    \"@types/supertest\": \"2.0.12\",\n    \"@typescript-eslint/eslint-plugin\": \"4.19.0\",\n    \"@typescript-eslint/parser\": \"4.19.0\",\n    \"commitizen\": \"4.2.3\",\n    \"conventional-changelog-cli\": \"2.1.1\",\n    \"coveralls\": \"3.1.1\",\n    \"eslint\": \"7.22.0\",\n    \"eslint-config-airbnb-base\": \"14.2.1\",\n    \"eslint-config-prettier\": \"8.1.0\",\n    \"eslint-plugin-import\": \"2.22.1\",\n    \"husky\": \"7.0.4\",\n    \"jest\": \"27.5.1\",\n    \"jest-extended\": \"2.0.0\",\n    \"lerna\": \"4.0.0\",\n    \"nodemon\": \"2.0.15\",\n    \"prettier\": \"2.6.1\",\n    \"pretty-quick\": \"3.1.3\",\n    \"rimraf\": \"3.0.2\",\n    \"supertest\": \"6.2.2\",\n    \"ts-jest\": \"27.1.4\",\n    \"ts-node\": \"10.7.0\",\n    \"tsconfig-extends\": \"1.0.1\",\n    \"tsconfig-paths\": \"3.14.1\",\n    \"typescript\": \"4.6.3\"\n  },\n  \"author\": {\n    \"name\": \"Michael Yali\",\n    \"email\": \"mihon4ik@gmail.com\"\n  }\n}\n"
  },
  {
    "path": "packages/crud/README.md",
    "content": "<div align=\"center\">\n  <h1>CRUD (@nestjsx/crud)</h1>\n</div>\n<div align=\"center\">\n  <strong>for RESTful APIs built with NestJs</strong>\n</div>\n\n<br />\n\n<div align=\"center\">\n  <a href=\"https://travis-ci.org/nestjsx/crud\">\n    <img src=\"https://github.com/nestjsx/crud/workflows/Tests/badge.svg\" alt=\"Build\" />\n  </a>\n  <a href=\"https://coveralls.io/github/nestjsx/crud?branch=master\">\n    <img src=\"https://coveralls.io/repos/github/nestjsx/crud/badge.svg\" alt=\"Coverage\" />\n  </a>\n  <a href=\"https://github.com/nestjsx/crud/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/nestjsx/crud.svg\" alt=\"License\" />\n  </a>\n  <a href=\"https://www.npmjs.com/package/@nestjsx/crud\">\n    <img src=\"https://img.shields.io/npm/v/@nestjsx/crud.svg\" alt=\"npm version\" />\n  </a>\n  <a href=\"https://www.npmjs.com/org/nestjsx\">\n    <img src=\"https://img.shields.io/npm/dm/@nestjsx/crud.svg\" alt=\"npm downloads\" />\n  </a>\n  <a href=\"https://npm.packagequality.com/#?package=@nestjsx%2Fcrud\">\n    <img src=\"https://npm.packagequality.com/shield/%40nestjsx%2Fcrud.svg\" alt=\"Package Quality\" />\n  </a>\n  <a href=\"https://renovatebot.com/\">\n    <img src=\"https://img.shields.io/badge/renovate-enabled-brightgreen.svg\" alt=\"Renovate\" />\n  </a>\n  <a href=\"http://makeapullrequest.com\">\n    <img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\" alt=\"PRs welcome\" />\n  </a>\n  <a href=\"https://github.com/marmelab/awesome-rest#nodejs\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-rest.svg?sanitize=true\" alt=\"Awesome REST\" />\n  </a>\n  <a href=\"https://github.com/juliandavidmr/awesome-nestjs#components--libraries\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-nest.svg?sanitize=true\" alt=\"Awesome Nest\" />\n  </a>\n  <a href=\"https://github.com/nestjs/nest\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/nest-powered.svg?sanitize=true\" alt=\"Nest Powered\" />\n  </a>\n  <a href=\"#individuals\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/backers/badge.svg\" />\n  </a>\n  <a href=\"#organizations\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/sponsors/badge.svg\" />\n  </a> \n</div>\n\n<div align=\"center\">\n  <sub>Built by\n  <a href=\"https://twitter.com/MichaelYali\">@MichaelYali</a> and\n  <a href=\"https://github.com/nestjsx/crud/graphs/contributors\">\n    Contributors\n  </a>\n</div>\n\n<br />\n\nWe believe that everyone who's working with NestJs and building some RESTful services and especially some CRUD functionality will find `@nestjsx/crud` microframework very useful.\n\n## Features\n\n<img align=\"right\" src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/crud-usage2.png\" alt=\"CRUD usage\" />\n\n- Super easy to install and start using the full-featured controllers and services :point_right:\n\n- DB and service agnostic extendable CRUD controllers\n\n- Reach query parsing with filtering, pagination, sorting, relations, nested relations, cache, etc.\n\n- Framework agnostic package with query builder for a frontend usage\n\n- Query, path params and DTOs validation included\n\n- Overriding controller methods with ease\n\n- Tiny config (including globally)\n\n- Additional helper decorators\n\n- Swagger documentation\n\n## Install\n\n```shell\nnpm i @nestjsx/crud class-transformer class-validator\n```\n\n## Packages\n\n- [**@nestjsx/crud**](https://www.npmjs.com/package/@nestjsx/crud) - core package which provides `@Crud()` decorator for endpoints generation, global configuration, validation, helper decorators ([docs](https://github.com/nestjsx/crud/wiki/Controllers#description))\n- [**@nestjsx/crud-request**](https://www.npmjs.com/package/@nestjsx/crud-request) - request builder/parser package which provides `RequestQueryBuilder` class for a frontend usage and `RequestQueryParser` that is being used internally for handling and validating query/path params on a backend side ([docs](https://github.com/nestjsx/crud/wiki/Requests#frontend-usage))\n- [**@nestjsx/crud-typeorm**](https://www.npmjs.com/package/@nestjsx/crud-typeorm) - TypeORM package which provides base `TypeOrmCrudService` with methods for CRUD database operations ([docs](https://github.com/nestjsx/crud/wiki/ServiceTypeorm))\n\n## Documentation\n\n- [General Information](https://github.com/nestjsx/crud/wiki#why)\n- [CRUD Controllers](https://github.com/nestjsx/crud/wiki/Controllers#description)\n- [CRUD ORM Services](https://github.com/nestjsx/crud/wiki/Services#description)\n- [Handling Requests](https://github.com/nestjsx/crud/wiki/Requests#description)\n\n## Support\n\nAny support is welcome. At least you can give us a star.\n\n## Contributors\n\n### Code Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].\n<a href=\"https://github.com/nestjsx/crud/graphs/contributors\"><img src=\"https://opencollective.com/nestjsx/contributors.svg?width=890&button=false\" /></a>\n\n### Financial Contributors\n\nBecome a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/nestjsx#backer)]\n\n#### Individuals\n\n<a href=\"https://opencollective.com/nestjsx#backers\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/backers.svg?width=890&button=false\"></a>\n\n#### Organizations\n\nSupport this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/nestjsx#sponsor)]\n\n<a href=\"https://opencollective.com/nestjsx/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/9/avatar.svg\"></a>\n\n## License\n\n[MIT](LICENSE)\n"
  },
  {
    "path": "packages/crud/package.json",
    "content": "{\n  \"name\": \"@nestjsx/crud\",\n  \"description\": \"NestJs CRUD for RESTful APIs\",\n  \"version\": \"5.0.0-alpha.3\",\n  \"license\": \"MIT\",\n  \"main\": \"lib/index.js\",\n  \"typings\": \"lib/index.d.ts\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"files\": [\n    \"lib\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/nestjsx/crud.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/nestjsx/crud/issues\"\n  },\n  \"keywords\": [\n    \"typescript\",\n    \"typeorm\",\n    \"nest\",\n    \"nestjs\",\n    \"rest\",\n    \"restful\",\n    \"api\",\n    \"crud\",\n    \"crud-generator\",\n    \"backend\",\n    \"frameworks\"\n  ],\n  \"author\": {\n    \"name\": \"Michael Yali\",\n    \"email\": \"mihon4ik@gmail.com\"\n  },\n  \"scripts\": {\n    \"build\": \"npx tsc -b\"\n  },\n  \"dependencies\": {\n    \"@nestjsx/crud-request\": \"^5.0.0-alpha.3\",\n    \"@nestjsx/util\": \"^5.0.0-alpha.3\",\n    \"deepmerge\": \"^3.2.0\",\n    \"pluralize\": \"^8.0.0\"\n  },\n  \"peerDependencies\": {\n    \"class-transformer\": \"*\",\n    \"class-validator\": \"*\"\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/constants.ts",
    "content": "export const FEAUTURE_NAME_METADATA = 'NESTJSX_FEAUTURE_NAME_METADATA';\nexport const ACTION_NAME_METADATA = 'NESTJSX_ACTION_NAME_METADATA';\nexport const OVERRIDE_METHOD_METADATA = 'NESTJSX_OVERRIDE_METHOD_METADATA';\nexport const PARSED_BODY_METADATA = 'NESTJSX_PARSED_BODY_METADATA';\nexport const PARSED_CRUD_REQUEST_KEY = 'NESTJSX_PARSED_CRUD_REQUEST_KEY';\nexport const CRUD_OPTIONS_METADATA = 'NESTJSX_CRUD_OPTIONS_METADATA';\nexport const CRUD_AUTH_OPTIONS_METADATA = 'NESTJSX_CRUD_AUTH_OPTIONS_METADATA';\n"
  },
  {
    "path": "packages/crud/src/crud/crud-routes.factory.ts",
    "content": "import { RequestMethod } from '@nestjs/common';\nimport { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum';\nimport {\n  isFalse,\n  isArrayFull,\n  isObjectFull,\n  isFunction,\n  objKeys,\n  isIn,\n  isEqual,\n  getOwnPropNames,\n  isNil,\n  isUndefined,\n} from '@nestjsx/util';\nimport * as deepmerge from 'deepmerge';\n\nimport { R } from './reflection.helper';\nimport { SerializeHelper } from './serialize.helper';\nimport { Swagger } from './swagger.helper';\nimport { Validation } from './validation.helper';\nimport { CrudRequestInterceptor, CrudResponseInterceptor } from '../interceptors';\nimport { BaseRoute, CrudOptions, CrudRequest, MergedCrudOptions } from '../interfaces';\nimport { BaseRouteName } from '../types';\nimport { CrudActions, CrudValidationGroups } from '../enums';\nimport { CrudConfigService } from '../module';\n\nexport class CrudRoutesFactory {\n  protected options: MergedCrudOptions;\n\n  protected swaggerModels: any = {};\n\n  constructor(protected target: any, options: CrudOptions) {\n    this.options = options;\n    this.create();\n  }\n\n  /* istanbul ignore next */\n  static create(target: any, options: CrudOptions): CrudRoutesFactory {\n    return new CrudRoutesFactory(target, options);\n  }\n\n  protected get targetProto(): any {\n    return this.target.prototype;\n  }\n\n  protected get modelName(): string {\n    return this.options.model.type.name;\n  }\n\n  protected get modelType(): any {\n    return this.options.model.type;\n  }\n\n  protected get actionsMap(): { [key in BaseRouteName]: CrudActions } {\n    return {\n      getManyBase: CrudActions.ReadAll,\n      getOneBase: CrudActions.ReadOne,\n      createManyBase: CrudActions.CreateMany,\n      createOneBase: CrudActions.CreateOne,\n      updateOneBase: CrudActions.UpdateOne,\n      deleteOneBase: CrudActions.DeleteOne,\n      replaceOneBase: CrudActions.ReplaceOne,\n      recoverOneBase: CrudActions.RecoverOne,\n    };\n  }\n\n  protected create() {\n    const routesSchema = this.getRoutesSchema();\n    this.mergeOptions();\n    this.setResponseModels();\n    this.createRoutes(routesSchema);\n    this.overrideRoutes(routesSchema);\n    this.enableRoutes(routesSchema);\n  }\n\n  protected mergeOptions() {\n    // merge auth config\n    const authOptions = R.getCrudAuthOptions(this.target);\n    this.options.auth = isObjectFull(authOptions) ? authOptions : {};\n    if (isUndefined(this.options.auth.property)) {\n      this.options.auth.property = CrudConfigService.config.auth.property;\n    }\n    if (isUndefined(this.options.auth.groups)) {\n      this.options.auth.groups = CrudConfigService.config.auth.groups;\n    }\n    if (isUndefined(this.options.auth.classTransformOptions)) {\n      this.options.auth.classTransformOptions = CrudConfigService.config.auth.classTransformOptions;\n    }\n\n    // merge query config\n    const query = isObjectFull(this.options.query) ? this.options.query : {};\n    this.options.query = { ...CrudConfigService.config.query, ...query };\n\n    // merge routes config\n    const routes = isObjectFull(this.options.routes) ? this.options.routes : {};\n    this.options.routes = deepmerge(CrudConfigService.config.routes, routes, {\n      arrayMerge: (a, b, c) => b,\n    });\n\n    // set params\n    this.options.params = isObjectFull(this.options.params)\n      ? this.options.params\n      : isObjectFull(CrudConfigService.config.params)\n      ? CrudConfigService.config.params\n      : {};\n    const hasPrimary = this.getPrimaryParams().length > 0;\n    if (!hasPrimary) {\n      this.options.params['id'] = {\n        field: 'id',\n        type: 'number',\n        primary: true,\n      };\n    }\n\n    // set dto\n    if (!isObjectFull(this.options.dto)) {\n      this.options.dto = {};\n    }\n\n    // set serialize\n    const serialize = isObjectFull(this.options.serialize) ? this.options.serialize : {};\n    this.options.serialize = { ...CrudConfigService.config.serialize, ...serialize };\n    this.options.serialize.get = isFalse(this.options.serialize.get)\n      ? false\n      : this.options.serialize.get || this.modelType;\n    this.options.serialize.getMany = isFalse(this.options.serialize.getMany)\n      ? false\n      : this.options.serialize.getMany\n      ? this.options.serialize.getMany\n      : isFalse(this.options.serialize.get)\n      ? /* istanbul ignore next */ false\n      : SerializeHelper.createGetManyDto(this.options.serialize.get, this.modelName);\n    this.options.serialize.create = isFalse(this.options.serialize.create)\n      ? false\n      : this.options.serialize.create || this.modelType;\n    this.options.serialize.update = isFalse(this.options.serialize.update)\n      ? false\n      : this.options.serialize.update || this.modelType;\n    this.options.serialize.replace = isFalse(this.options.serialize.replace)\n      ? false\n      : this.options.serialize.replace || this.modelType;\n    this.options.serialize.delete =\n      isFalse(this.options.serialize.delete) || !this.options.routes.deleteOneBase.returnDeleted\n        ? false\n        : this.options.serialize.delete || this.modelType;\n\n    R.setCrudOptions(this.options, this.target);\n  }\n\n  protected getRoutesSchema(): BaseRoute[] {\n    return [\n      {\n        name: 'getOneBase',\n        path: '/',\n        method: RequestMethod.GET,\n        enable: false,\n        override: false,\n        withParams: true,\n      },\n      {\n        name: 'getManyBase',\n        path: '/',\n        method: RequestMethod.GET,\n        enable: false,\n        override: false,\n        withParams: false,\n      },\n      {\n        name: 'createOneBase',\n        path: '/',\n        method: RequestMethod.POST,\n        enable: false,\n        override: false,\n        withParams: false,\n      },\n      {\n        name: 'createManyBase',\n        path: '/bulk',\n        method: RequestMethod.POST,\n        enable: false,\n        override: false,\n        withParams: false,\n      },\n      {\n        name: 'updateOneBase',\n        path: '/',\n        method: RequestMethod.PATCH,\n        enable: false,\n        override: false,\n        withParams: true,\n      },\n      {\n        name: 'replaceOneBase',\n        path: '/',\n        method: RequestMethod.PUT,\n        enable: false,\n        override: false,\n        withParams: true,\n      },\n      {\n        name: 'deleteOneBase',\n        path: '/',\n        method: RequestMethod.DELETE,\n        enable: false,\n        override: false,\n        withParams: true,\n      },\n      {\n        name: 'recoverOneBase',\n        path: '/recover',\n        method: RequestMethod.PATCH,\n        enable: false,\n        override: false,\n        withParams: true,\n      },\n    ];\n  }\n\n  protected getManyBase(name: BaseRouteName) {\n    this.targetProto[name] = function getManyBase(req: CrudRequest) {\n      return this.service.getMany(req);\n    };\n  }\n\n  protected getOneBase(name: BaseRouteName) {\n    this.targetProto[name] = function getOneBase(req: CrudRequest) {\n      return this.service.getOne(req);\n    };\n  }\n\n  protected createOneBase(name: BaseRouteName) {\n    this.targetProto[name] = function createOneBase(req: CrudRequest, dto: any) {\n      return this.service.createOne(req, dto);\n    };\n  }\n\n  protected createManyBase(name: BaseRouteName) {\n    this.targetProto[name] = function createManyBase(req: CrudRequest, dto: any) {\n      return this.service.createMany(req, dto);\n    };\n  }\n\n  protected updateOneBase(name: BaseRouteName) {\n    this.targetProto[name] = function updateOneBase(req: CrudRequest, dto: any) {\n      return this.service.updateOne(req, dto);\n    };\n  }\n\n  protected replaceOneBase(name: BaseRouteName) {\n    this.targetProto[name] = function replaceOneBase(req: CrudRequest, dto: any) {\n      return this.service.replaceOne(req, dto);\n    };\n  }\n\n  protected deleteOneBase(name: BaseRouteName) {\n    this.targetProto[name] = function deleteOneBase(req: CrudRequest) {\n      return this.service.deleteOne(req);\n    };\n  }\n\n  protected recoverOneBase(name: BaseRouteName) {\n    this.targetProto[name] = function recoverOneBase(req: CrudRequest) {\n      return this.service.recoverOne(req);\n    };\n  }\n\n  protected canCreateRoute(name: BaseRouteName) {\n    const only = this.options.routes.only;\n    const exclude = this.options.routes.exclude;\n\n    // include recover route only for models with soft delete option\n    if (name === 'recoverOneBase' && this.options.query.softDelete !== true) {\n      return false;\n    }\n\n    if (isArrayFull(only)) {\n      return only.some((route) => route === name);\n    }\n\n    if (isArrayFull(exclude)) {\n      return !exclude.some((route) => route === name);\n    }\n\n    return true;\n  }\n\n  protected setResponseModels() {\n    const modelType = isFunction(this.modelType)\n      ? this.modelType\n      : SerializeHelper.createGetOneResponseDto(this.modelName);\n    this.swaggerModels.get = isFunction(this.options.serialize.get) ? this.options.serialize.get : modelType;\n    this.swaggerModels.getMany =\n      this.options.serialize.getMany || SerializeHelper.createGetManyDto(this.swaggerModels.get, this.modelName);\n    this.swaggerModels.create = isFunction(this.options.serialize.create) ? this.options.serialize.create : modelType;\n    this.swaggerModels.update = isFunction(this.options.serialize.update) ? this.options.serialize.update : modelType;\n    this.swaggerModels.replace = isFunction(this.options.serialize.replace)\n      ? this.options.serialize.replace\n      : modelType;\n    this.swaggerModels.delete = isFunction(this.options.serialize.delete) ? this.options.serialize.delete : modelType;\n    this.swaggerModels.recover = isFunction(this.options.serialize.recover)\n      ? this.options.serialize.recover\n      : modelType;\n    Swagger.setExtraModels(this.swaggerModels);\n  }\n\n  protected createRoutes(routesSchema: BaseRoute[]) {\n    const primaryParams = this.getPrimaryParams().filter((param) => !this.options.params[param].disabled);\n\n    routesSchema.forEach((route) => {\n      if (this.canCreateRoute(route.name)) {\n        // create base method\n        this[route.name](route.name);\n        route.enable = true;\n        // set metadata\n        this.setBaseRouteMeta(route.name);\n      }\n\n      if (route.withParams && primaryParams.length > 0) {\n        route.path =\n          route.path !== '/'\n            ? `${primaryParams.map((param) => `/:${param}`).join('')}${route.path}`\n            : primaryParams.map((param) => `/:${param}`).join('');\n      }\n    });\n  }\n\n  protected overrideRoutes(routesSchema: BaseRoute[]) {\n    getOwnPropNames(this.targetProto).forEach((name) => {\n      const override = R.getOverrideRoute(this.targetProto[name]);\n      const route = routesSchema.find((r) => isEqual(r.name, override));\n\n      if (override && route && route.enable) {\n        // get metadata\n        const interceptors = R.getInterceptors(this.targetProto[name]);\n        const baseInterceptors = R.getInterceptors(this.targetProto[override]);\n        const baseAction = R.getAction(this.targetProto[override]);\n        const operation = Swagger.getOperation(this.targetProto[name]);\n        const baseOperation = Swagger.getOperation(this.targetProto[override]);\n        const swaggerParams = Swagger.getParams(this.targetProto[name]);\n        const baseSwaggerParams = Swagger.getParams(this.targetProto[override]);\n        const responseOk = Swagger.getResponseOk(this.targetProto[name]);\n        const baseResponseOk = Swagger.getResponseOk(this.targetProto[override]);\n        // set metadata\n        R.setInterceptors([...baseInterceptors, ...interceptors], this.targetProto[name]);\n        R.setAction(baseAction, this.targetProto[name]);\n        Swagger.setOperation({ ...baseOperation, ...operation }, this.targetProto[name]);\n        Swagger.setParams([...baseSwaggerParams, ...swaggerParams], this.targetProto[name]);\n        Swagger.setResponseOk({ ...baseResponseOk, ...responseOk }, this.targetProto[name]);\n        this.overrideParsedBodyDecorator(override, name);\n        // enable route\n        R.setRoute(route, this.targetProto[name]);\n        route.override = true;\n      }\n    });\n  }\n\n  protected enableRoutes(routesSchema: BaseRoute[]) {\n    routesSchema.forEach((route) => {\n      if (!route.override && route.enable) {\n        R.setRoute(route, this.targetProto[route.name]);\n      }\n    });\n  }\n\n  protected overrideParsedBodyDecorator(override: BaseRouteName, name: string) {\n    const allowed = ['createManyBase', 'createOneBase', 'updateOneBase', 'replaceOneBase'] as BaseRouteName[];\n    const withBody = isIn(override, allowed);\n    const parsedBody = R.getParsedBody(this.targetProto[name]);\n\n    if (withBody && parsedBody) {\n      const baseKey = `${RouteParamtypes.BODY}:1`;\n      const key = `${RouteParamtypes.BODY}:${parsedBody.index}`;\n      const baseRouteArgs = R.getRouteArgs(this.target, override);\n      const routeArgs = R.getRouteArgs(this.target, name);\n      const baseBodyArg = baseRouteArgs[baseKey];\n      R.setRouteArgs(\n        {\n          ...routeArgs,\n          [key]: {\n            ...baseBodyArg,\n            index: parsedBody.index,\n          },\n        },\n        this.target,\n        name,\n      );\n\n      /* istanbul ignore else */\n      if (isEqual(override, 'createManyBase')) {\n        const paramTypes = R.getRouteArgsTypes(this.targetProto, name);\n        const metatype = paramTypes[parsedBody.index];\n        const types = [String, Boolean, Number, Array, Object];\n        const toCopy = isIn(metatype, types) || /* istanbul ignore next */ isNil(metatype);\n\n        /* istanbul ignore else */\n        if (toCopy) {\n          const baseParamTypes = R.getRouteArgsTypes(this.targetProto, override);\n          const baseMetatype = baseParamTypes[1];\n          paramTypes.splice(parsedBody.index, 1, baseMetatype);\n          R.setRouteArgsTypes(paramTypes, this.targetProto, name);\n        }\n      }\n    }\n  }\n\n  protected getPrimaryParams(): string[] {\n    return objKeys(this.options.params).filter(\n      (param) => this.options.params[param] && this.options.params[param].primary,\n    );\n  }\n\n  protected setBaseRouteMeta(name: BaseRouteName) {\n    this.setRouteArgs(name);\n    this.setRouteArgsTypes(name);\n    this.setInterceptors(name);\n    this.setAction(name);\n    this.setSwaggerOperation(name);\n    this.setSwaggerPathParams(name);\n    this.setSwaggerQueryParams(name);\n    this.setSwaggerResponseOk(name);\n    // set decorators after Swagger so metadata can be overwritten\n    this.setDecorators(name);\n  }\n\n  protected setRouteArgs(name: BaseRouteName) {\n    let rest = {};\n    const routes: BaseRouteName[] = ['createManyBase', 'createOneBase', 'updateOneBase', 'replaceOneBase'];\n\n    if (isIn(name, routes)) {\n      const action = this.routeNameAction(name);\n      const hasDto = !isNil(this.options.dto[action]);\n      const { UPDATE, CREATE } = CrudValidationGroups;\n      const groupEnum = isIn(name, ['updateOneBase', 'replaceOneBase']) ? UPDATE : CREATE;\n      const group = !hasDto ? groupEnum : undefined;\n\n      rest = R.setBodyArg(1, [Validation.getValidationPipe(this.options, group)]);\n    }\n\n    R.setRouteArgs({ ...R.setParsedRequestArg(0), ...rest }, this.target, name);\n  }\n\n  protected setRouteArgsTypes(name: BaseRouteName) {\n    if (isEqual(name, 'createManyBase')) {\n      const bulkDto = Validation.createBulkDto(this.options);\n      R.setRouteArgsTypes([Object, bulkDto], this.targetProto, name);\n    } else if (isIn(name, ['createOneBase', 'updateOneBase', 'replaceOneBase'])) {\n      const action = this.routeNameAction(name);\n      const dto = this.options.dto[action] || this.modelType;\n      R.setRouteArgsTypes([Object, dto], this.targetProto, name);\n    } else {\n      R.setRouteArgsTypes([Object], this.targetProto, name);\n    }\n  }\n\n  protected setInterceptors(name: BaseRouteName) {\n    const interceptors = this.options.routes[name].interceptors;\n    R.setInterceptors(\n      [\n        CrudRequestInterceptor,\n        CrudResponseInterceptor,\n        ...(isArrayFull(interceptors) ? /* istanbul ignore next */ interceptors : []),\n      ],\n      this.targetProto[name],\n    );\n  }\n\n  protected setDecorators(name: BaseRouteName) {\n    const decorators = this.options.routes[name].decorators;\n    R.setDecorators(isArrayFull(decorators) ? /* istanbul ignore next */ decorators : [], this.targetProto, name);\n  }\n\n  protected setAction(name: BaseRouteName) {\n    R.setAction(this.actionsMap[name], this.targetProto[name]);\n  }\n\n  protected setSwaggerOperation(name: BaseRouteName) {\n    const summary = Swagger.operationsMap(this.modelName)[name];\n    const operationId = name + this.targetProto.constructor.name + this.modelName;\n    Swagger.setOperation({ summary, operationId }, this.targetProto[name]);\n  }\n\n  protected setSwaggerPathParams(name: BaseRouteName) {\n    const metadata = Swagger.getParams(this.targetProto[name]);\n    const withoutPrimary: BaseRouteName[] = ['createManyBase', 'createOneBase', 'getManyBase'];\n\n    const removePrimary = isIn(name, withoutPrimary);\n    const params = objKeys(this.options.params)\n      .filter((key) => !this.options.params[key].disabled)\n      .filter((key) => !(removePrimary && this.options.params[key].primary))\n      .reduce((a, c) => ({ ...a, [c]: this.options.params[c] }), {});\n\n    const pathParamsMeta = Swagger.createPathParamsMeta(params);\n    Swagger.setParams([...metadata, ...pathParamsMeta], this.targetProto[name]);\n  }\n\n  protected setSwaggerQueryParams(name: BaseRouteName) {\n    const metadata = Swagger.getParams(this.targetProto[name]);\n    const queryParamsMeta = Swagger.createQueryParamsMeta(name, this.options);\n    Swagger.setParams([...metadata, ...queryParamsMeta], this.targetProto[name]);\n  }\n\n  protected setSwaggerResponseOk(name: BaseRouteName) {\n    const metadata = Swagger.getResponseOk(this.targetProto[name]);\n    const metadataToAdd =\n      Swagger.createResponseMeta(name, this.options, this.swaggerModels) || /* istanbul ignore next */ {};\n    Swagger.setResponseOk({ ...metadata, ...metadataToAdd }, this.targetProto[name]);\n  }\n\n  protected routeNameAction(name: BaseRouteName): string {\n    return name.split('OneBase')[0] || /* istanbul ignore next */ name.split('ManyBase')[0];\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/crud/index.ts",
    "content": "export * from './crud-routes.factory';\nexport * from './reflection.helper';\nexport * from './swagger.helper';\nexport * from './validation.helper';\n"
  },
  {
    "path": "packages/crud/src/crud/reflection.helper.ts",
    "content": "import { RouteParamtypes } from '@nestjs/common/enums/route-paramtypes.enum';\nimport {\n  CUSTOM_ROUTE_AGRS_METADATA,\n  INTERCEPTORS_METADATA,\n  METHOD_METADATA,\n  PARAMTYPES_METADATA,\n  PATH_METADATA,\n  ROUTE_ARGS_METADATA,\n} from '@nestjs/common/constants';\nimport { ArgumentsHost } from '@nestjs/common';\nimport { isFunction } from '@nestjsx/util';\n\nimport { BaseRoute, MergedCrudOptions, AuthOptions } from '../interfaces';\nimport { BaseRouteName } from '../types';\nimport {\n  CRUD_OPTIONS_METADATA,\n  ACTION_NAME_METADATA,\n  PARSED_CRUD_REQUEST_KEY,\n  PARSED_BODY_METADATA,\n  OVERRIDE_METHOD_METADATA,\n  CRUD_AUTH_OPTIONS_METADATA,\n} from '../constants';\nimport { CrudActions } from '../enums';\n\nexport class R {\n  static set(metadataKey: any, metadataValue: any, target: unknown, propertyKey: string | symbol = undefined) {\n    if (propertyKey) {\n      Reflect.defineMetadata(metadataKey, metadataValue, target, propertyKey);\n    } else {\n      Reflect.defineMetadata(metadataKey, metadataValue, target);\n    }\n  }\n\n  static get<T extends any>(metadataKey: any, target: unknown, propertyKey: string | symbol = undefined): T {\n    return propertyKey\n      ? Reflect.getMetadata(metadataKey, target, propertyKey)\n      : Reflect.getMetadata(metadataKey, target);\n  }\n\n  static createCustomRouteArg(\n    paramtype: string,\n    index: number,\n    /* istanbul ignore next */\n    pipes: any[] = [],\n    data = undefined,\n  ): any {\n    return {\n      [`${paramtype}${CUSTOM_ROUTE_AGRS_METADATA}:${index}`]: {\n        index,\n        factory: (_, ctx) => R.getContextRequest(ctx)[paramtype],\n        data,\n        pipes,\n      },\n    };\n  }\n\n  static createRouteArg(\n    paramtype: RouteParamtypes,\n    index: number,\n    /* istanbul ignore next */\n    pipes: any[] = [],\n    data = undefined,\n  ): any {\n    return {\n      [`${paramtype}:${index}`]: {\n        index,\n        pipes,\n        data,\n      },\n    };\n  }\n\n  static setDecorators(decorators: (PropertyDecorator | MethodDecorator)[], target: any, name: string) {\n    // this makes metadata decorator works\n    const decoratedDescriptor = Reflect.decorate(\n      decorators,\n      target,\n      name,\n      Reflect.getOwnPropertyDescriptor(target, name),\n    );\n\n    // this makes proxy decorator works\n    Reflect.defineProperty(target, name, decoratedDescriptor);\n  }\n\n  static setParsedRequestArg(index: number) {\n    return R.createCustomRouteArg(PARSED_CRUD_REQUEST_KEY, index);\n  }\n\n  static setBodyArg(index: number, /* istanbul ignore next */ pipes: any[] = []) {\n    return R.createRouteArg(RouteParamtypes.BODY, index, pipes);\n  }\n\n  static setCrudOptions(options: MergedCrudOptions, target: any) {\n    R.set(CRUD_OPTIONS_METADATA, options, target);\n  }\n\n  static setRoute(route: BaseRoute, func: unknown) {\n    R.set(PATH_METADATA, route.path, func);\n    R.set(METHOD_METADATA, route.method, func);\n  }\n\n  static setInterceptors(interceptors: any[], func: unknown) {\n    R.set(INTERCEPTORS_METADATA, interceptors, func);\n  }\n\n  static setRouteArgs(metadata: any, target: any, name: string) {\n    R.set(ROUTE_ARGS_METADATA, metadata, target, name);\n  }\n\n  static setRouteArgsTypes(metadata: any, target: any, name: string) {\n    R.set(PARAMTYPES_METADATA, metadata, target, name);\n  }\n\n  static setAction(action: CrudActions, func: unknown) {\n    R.set(ACTION_NAME_METADATA, action, func);\n  }\n\n  static setCrudAuthOptions(metadata: any, target: any) {\n    R.set(CRUD_AUTH_OPTIONS_METADATA, metadata, target);\n  }\n\n  static getCrudAuthOptions(target: any): AuthOptions {\n    return R.get(CRUD_AUTH_OPTIONS_METADATA, target);\n  }\n\n  static getCrudOptions(target: any): MergedCrudOptions {\n    return R.get(CRUD_OPTIONS_METADATA, target);\n  }\n\n  static getAction(func: unknown): CrudActions {\n    return R.get(ACTION_NAME_METADATA, func);\n  }\n\n  static getOverrideRoute(func: unknown): BaseRouteName {\n    return R.get(OVERRIDE_METHOD_METADATA, func);\n  }\n\n  static getInterceptors(func: unknown): any[] {\n    return R.get(INTERCEPTORS_METADATA, func) || [];\n  }\n\n  static getRouteArgs(target: any, name: string): any {\n    return R.get(ROUTE_ARGS_METADATA, target, name);\n  }\n\n  static getRouteArgsTypes(target: any, name: string): any[] {\n    return R.get(PARAMTYPES_METADATA, target, name) || /* istanbul ignore next */ [];\n  }\n\n  static getParsedBody(func: unknown): any {\n    return R.get(PARSED_BODY_METADATA, func);\n  }\n\n  static getContextRequest(ctx: ArgumentsHost): any {\n    return isFunction(ctx.switchToHttp) ? ctx.switchToHttp().getRequest() : /* istanbul ignore next */ ctx;\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/crud/serialize.helper.ts",
    "content": "import { Type } from 'class-transformer';\nimport { GetManyDefaultResponse } from '../interfaces';\nimport { ApiProperty } from './swagger.helper';\n\nexport class SerializeHelper {\n  static createGetManyDto(dto: any, resourceName: string): any {\n    class GetManyResponseDto implements GetManyDefaultResponse<any> {\n      @ApiProperty({ type: dto, isArray: true })\n      @Type(() => dto)\n      data: any[];\n\n      @ApiProperty({ type: 'number' })\n      count: number;\n\n      @ApiProperty({ type: 'number' })\n      total: number;\n\n      @ApiProperty({ type: 'number' })\n      page: number;\n\n      @ApiProperty({ type: 'number' })\n      pageCount: number;\n    }\n\n    Object.defineProperty(GetManyResponseDto, 'name', {\n      writable: false,\n      value: `GetMany${resourceName}ResponseDto`,\n    });\n\n    return GetManyResponseDto;\n  }\n\n  static createGetOneResponseDto(resourceName: string): any {\n    class GetOneResponseDto {}\n\n    Object.defineProperty(GetOneResponseDto, 'name', {\n      writable: false,\n      value: `${resourceName}ResponseDto`,\n    });\n\n    return GetOneResponseDto;\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/crud/swagger.helper.ts",
    "content": "import { HttpStatus } from '@nestjs/common';\nimport { RequestQueryBuilder } from '@nestjsx/crud-request';\nimport { isString, objKeys } from '@nestjsx/util';\nimport { MergedCrudOptions, ParamsOptions } from '../interfaces';\nimport { BaseRouteName } from '../types';\nimport { safeRequire } from '../util';\nimport { R } from './reflection.helper';\nconst pluralize = require('pluralize');\n\nexport const swagger = safeRequire('@nestjs/swagger', () => require('@nestjs/swagger'));\nexport const swaggerConst = safeRequire('@nestjs/swagger/dist/constants', () =>\n  require('@nestjs/swagger/dist/constants'),\n);\nexport const swaggerPkgJson = safeRequire('@nestjs/swagger/package.json', () =>\n  require('@nestjs/swagger/package.json'),\n);\n\nexport class Swagger {\n  static operationsMap(modelName: string): { [key in BaseRouteName]: string } {\n    return {\n      getManyBase: `Retrieve multiple ${pluralize(modelName)}`,\n      getOneBase: `Retrieve a single ${modelName}`,\n      createManyBase: `Create multiple ${pluralize(modelName)}`,\n      createOneBase: `Create a single ${modelName}`,\n      updateOneBase: `Update a single ${modelName}`,\n      replaceOneBase: `Replace a single ${modelName}`,\n      deleteOneBase: `Delete a single ${modelName}`,\n      recoverOneBase: `Recover one ${modelName}`,\n    };\n  }\n\n  static setOperation(metadata: unknown, func: any): void {\n    /* istanbul ignore else */\n    if (swaggerConst) {\n      R.set(swaggerConst.DECORATORS.API_OPERATION, metadata, func);\n    }\n  }\n\n  static setParams(metadata: unknown, func: any): void {\n    /* istanbul ignore else */\n    if (swaggerConst) {\n      R.set(swaggerConst.DECORATORS.API_PARAMETERS, metadata, func);\n    }\n  }\n\n  static setExtraModels(swaggerModels: any): void {\n    /* istanbul ignore else */\n    if (swaggerConst) {\n      const meta = Swagger.getExtraModels(swaggerModels.get);\n      const models: any[] = [\n        ...meta,\n        ...objKeys(swaggerModels)\n          .map((name) => swaggerModels[name])\n          .filter((one) => one && one.name !== swaggerModels.get.name),\n      ];\n      R.set(swaggerConst.DECORATORS.API_EXTRA_MODELS, models, swaggerModels.get);\n    }\n  }\n\n  static setResponseOk(metadata: unknown, func: any): void {\n    /* istanbul ignore else */\n    if (swaggerConst) {\n      R.set(swaggerConst.DECORATORS.API_RESPONSE, metadata, func);\n    }\n  }\n\n  static getOperation(func: any): any {\n    /* istanbul ignore next */\n    return swaggerConst ? R.get(swaggerConst.DECORATORS.API_OPERATION, func) || {} : {};\n  }\n\n  static getParams(func: any): any[] {\n    /* istanbul ignore next */\n    return swaggerConst ? R.get(swaggerConst.DECORATORS.API_PARAMETERS, func) || [] : [];\n  }\n\n  static getExtraModels(target: unknown): any[] {\n    /* istanbul ignore next */\n    return swaggerConst ? R.get(swaggerConst.API_EXTRA_MODELS, target) || [] : [];\n  }\n\n  static getResponseOk(func: any): any {\n    /* istanbul ignore next */\n    return swaggerConst ? R.get(swaggerConst.DECORATORS.API_RESPONSE, func) || {} : {};\n  }\n\n  static createResponseMeta(name: BaseRouteName, options: MergedCrudOptions, swaggerModels: any): any {\n    /* istanbul ignore else */\n    if (swagger) {\n      const { routes, query } = options;\n      const oldVersion = Swagger.getSwaggerVersion() < 4;\n\n      switch (name) {\n        case 'getOneBase':\n          return {\n            [HttpStatus.OK]: {\n              description: 'Get one base response',\n              type: swaggerModels.get,\n            },\n          };\n        case 'getManyBase':\n          /* istanbul ignore if */\n          if (oldVersion) {\n            return {\n              [HttpStatus.OK]: {\n                type: swaggerModels.getMany,\n              },\n            };\n          }\n\n          return {\n            [HttpStatus.OK]: query.alwaysPaginate\n              ? {\n                  description: 'Get paginated response',\n                  type: swaggerModels.getMany,\n                }\n              : {\n                  description: 'Get many base response',\n                  schema: {\n                    oneOf: [\n                      { $ref: swagger.getSchemaPath(swaggerModels.getMany.name) },\n                      {\n                        type: 'array',\n                        items: { $ref: swagger.getSchemaPath(swaggerModels.get.name) },\n                      },\n                    ],\n                  },\n                },\n          };\n        case 'createOneBase':\n          /* istanbul ignore if */\n          if (oldVersion) {\n            return {\n              [HttpStatus.OK]: {\n                type: swaggerModels.create,\n              },\n            };\n          }\n\n          return {\n            [HttpStatus.CREATED]: {\n              description: 'Get create one base response',\n              schema: { $ref: swagger.getSchemaPath(swaggerModels.create.name) },\n            },\n          };\n        case 'createManyBase':\n          /* istanbul ignore if */\n          if (oldVersion) {\n            return {\n              [HttpStatus.OK]: {\n                type: swaggerModels.create,\n                isArray: true,\n              },\n            };\n          }\n\n          return {\n            [HttpStatus.CREATED]: swaggerModels.createMany\n              ? /* istanbul ignore next */ {\n                  description: 'Get create many base response',\n                  schema: { $ref: swagger.getSchemaPath(swaggerModels.createMany.name) },\n                }\n              : {\n                  description: 'Get create many base response',\n                  schema: {\n                    type: 'array',\n                    items: { $ref: swagger.getSchemaPath(swaggerModels.create.name) },\n                  },\n                },\n          };\n        case 'deleteOneBase':\n          /* istanbul ignore if */\n          if (oldVersion) {\n            return {\n              [HttpStatus.OK]: routes.deleteOneBase.returnDeleted\n                ? {\n                    type: swaggerModels.delete,\n                  }\n                : {},\n            };\n          }\n          return {\n            [HttpStatus.OK]: routes.deleteOneBase.returnDeleted\n              ? {\n                  description: 'Delete one base response',\n                  schema: { $ref: swagger.getSchemaPath(swaggerModels.delete.name) },\n                }\n              : {\n                  description: 'Delete one base response',\n                },\n          };\n        case 'recoverOneBase':\n          /* istanbul ignore if */\n          if (oldVersion) {\n            return {\n              [HttpStatus.OK]: routes.recoverOneBase.returnRecovered\n                ? {\n                    type: swaggerModels.delete,\n                  }\n                : {},\n            };\n          }\n          return {\n            [HttpStatus.OK]: routes.recoverOneBase.returnRecovered\n              ? {\n                  description: 'Recover one base response',\n                  schema: { $ref: swagger.getSchemaPath(swaggerModels.recover.name) },\n                }\n              : {\n                  description: 'Recover one base response',\n                },\n          };\n        default:\n          const dto = swaggerModels[name.split('OneBase')[0]];\n\n          /* istanbul ignore if */\n          if (oldVersion) {\n            return {\n              [HttpStatus.OK]: {\n                type: dto,\n              },\n            };\n          }\n\n          return {\n            [HttpStatus.OK]: {\n              description: 'Response',\n              schema: { $ref: swagger.getSchemaPath(dto.name) },\n            },\n          };\n      }\n    } else {\n      return {};\n    }\n  }\n\n  static createPathParamsMeta(options: ParamsOptions): any[] {\n    return swaggerConst\n      ? objKeys(options).map((param) => ({\n          name: param,\n          required: true,\n          in: 'path',\n          type: options[param].type === 'number' ? Number : String,\n          enum: options[param].enum ? Object.values(options[param].enum) : undefined,\n        }))\n      : /* istanbul ignore next */ [];\n  }\n\n  static createQueryParamsMeta(name: BaseRouteName, options: MergedCrudOptions) {\n    /* istanbul ignore if */\n    if (!swaggerConst) {\n      return [];\n    }\n\n    const {\n      delim: d,\n      delimStr: coma,\n      fields,\n      search,\n      filter,\n      or,\n      join,\n      sort,\n      limit,\n      offset,\n      page,\n      cache,\n      includeDeleted,\n    } = Swagger.getQueryParamsNames();\n    const oldVersion = Swagger.getSwaggerVersion() < 4;\n    const docsLink = (a: string) =>\n      `<a href=\"https://github.com/nestjsx/crud/wiki/Requests#${a}\" target=\"_blank\">Docs</a>`;\n\n    const fieldsMetaBase = {\n      name: fields,\n      description: `Selects resource fields. ${docsLink('select')}`,\n      required: false,\n      in: 'query',\n    };\n    const fieldsMeta = oldVersion\n      ? /* istanbul ignore next */ {\n          ...fieldsMetaBase,\n          type: 'array',\n          items: {\n            type: 'string',\n          },\n          collectionFormat: 'csv',\n        }\n      : {\n          ...fieldsMetaBase,\n          schema: {\n            type: 'array',\n            items: {\n              type: 'string',\n            },\n          },\n          style: 'form',\n          explode: false,\n        };\n\n    const searchMetaBase = {\n      name: search,\n      description: `Adds search condition. ${docsLink('search')}`,\n      required: false,\n      in: 'query',\n    };\n    const searchMeta = oldVersion\n      ? /* istanbul ignore next */ { ...searchMetaBase, type: 'string' }\n      : { ...searchMetaBase, schema: { type: 'string' } };\n\n    const filterMetaBase = {\n      name: filter,\n      description: `Adds filter condition. ${docsLink('filter')}`,\n      required: false,\n      in: 'query',\n    };\n    const filterMeta = oldVersion\n      ? /* istanbul ignore next */ {\n          ...filterMetaBase,\n          items: {\n            type: 'string',\n          },\n          type: 'array',\n          collectionFormat: 'multi',\n        }\n      : {\n          ...filterMetaBase,\n          schema: {\n            type: 'array',\n            items: {\n              type: 'string',\n            },\n          },\n          style: 'form',\n          explode: true,\n        };\n\n    const orMetaBase = {\n      name: or,\n      description: `Adds OR condition. ${docsLink('or')}`,\n      required: false,\n      in: 'query',\n    };\n    const orMeta = oldVersion\n      ? /* istanbul ignore next */ {\n          ...orMetaBase,\n          items: {\n            type: 'string',\n          },\n          type: 'array',\n          collectionFormat: 'multi',\n        }\n      : {\n          ...orMetaBase,\n          schema: {\n            type: 'array',\n            items: {\n              type: 'string',\n            },\n          },\n          style: 'form',\n          explode: true,\n        };\n\n    const sortMetaBase = {\n      name: sort,\n      description: `Adds sort by field. ${docsLink('sort')}`,\n      required: false,\n      in: 'query',\n    };\n    const sortMeta = oldVersion\n      ? /* istanbul ignore next */ {\n          ...sortMetaBase,\n          items: {\n            type: 'string',\n          },\n          type: 'array',\n          collectionFormat: 'multi',\n        }\n      : {\n          ...sortMetaBase,\n          schema: {\n            type: 'array',\n            items: {\n              type: 'string',\n            },\n          },\n          style: 'form',\n          explode: true,\n        };\n\n    const joinMetaBase = {\n      name: join,\n      description: `Adds relational resources. ${docsLink('join')}`,\n      required: false,\n      in: 'query',\n    };\n    const joinMeta = oldVersion\n      ? /* istanbul ignore next */ {\n          ...joinMetaBase,\n          items: {\n            type: 'string',\n          },\n          type: 'array',\n          collectionFormat: 'multi',\n        }\n      : {\n          ...joinMetaBase,\n          schema: {\n            type: 'array',\n            items: {\n              type: 'string',\n            },\n          },\n          style: 'form',\n          explode: true,\n        };\n\n    const limitMetaBase = {\n      name: limit,\n      description: `Limit amount of resources. ${docsLink('limit')}`,\n      required: false,\n      in: 'query',\n    };\n    const limitMeta = oldVersion\n      ? /* istanbul ignore next */ { ...limitMetaBase, type: 'integer' }\n      : { ...limitMetaBase, schema: { type: 'integer' } };\n\n    const offsetMetaBase = {\n      name: offset,\n      description: `Offset amount of resources. ${docsLink('offset')}`,\n      required: false,\n      in: 'query',\n    };\n    const offsetMeta = oldVersion\n      ? /* istanbul ignore next */ { ...offsetMetaBase, type: 'integer' }\n      : { ...offsetMetaBase, schema: { type: 'integer' } };\n\n    const pageMetaBase = {\n      name: page,\n      description: `Page portion of resources. ${docsLink('page')}`,\n      required: false,\n      in: 'query',\n    };\n    const pageMeta = oldVersion\n      ? /* istanbul ignore next */ { ...pageMetaBase, type: 'integer' }\n      : { ...pageMetaBase, schema: { type: 'integer' } };\n\n    const cacheMetaBase = {\n      name: cache,\n      description: `Reset cache (if was enabled). ${docsLink('cache')}`,\n      required: false,\n      in: 'query',\n    };\n    const cacheMeta = oldVersion\n      ? /* istanbul ignore next */ {\n          ...cacheMetaBase,\n          type: 'integer',\n          minimum: 0,\n          maximum: 1,\n        }\n      : { ...cacheMetaBase, schema: { type: 'integer', minimum: 0, maximum: 1 } };\n\n    const includeDeletedMetaBase = {\n      name: includeDeleted,\n      description: `Include deleted. ${docsLink('includeDeleted')}`,\n      required: false,\n      in: 'query',\n    };\n    const includeDeletedMeta = oldVersion\n      ? /* istanbul ignore next */ {\n          ...includeDeletedMetaBase,\n          type: 'integer',\n          minimum: 0,\n          maximum: 1,\n        }\n      : {\n          ...includeDeletedMetaBase,\n          schema: { type: 'integer', minimum: 0, maximum: 1 },\n        };\n\n    switch (name) {\n      case 'getManyBase':\n        return options.query.softDelete\n          ? [\n              fieldsMeta,\n              searchMeta,\n              filterMeta,\n              orMeta,\n              sortMeta,\n              joinMeta,\n              limitMeta,\n              offsetMeta,\n              pageMeta,\n              cacheMeta,\n              includeDeletedMeta,\n            ]\n          : [\n              fieldsMeta,\n              searchMeta,\n              filterMeta,\n              orMeta,\n              sortMeta,\n              joinMeta,\n              limitMeta,\n              offsetMeta,\n              pageMeta,\n              cacheMeta,\n            ];\n      case 'getOneBase':\n        return options.query.softDelete\n          ? [fieldsMeta, joinMeta, cacheMeta, includeDeletedMeta]\n          : [fieldsMeta, joinMeta, cacheMeta];\n      default:\n        return [];\n    }\n  }\n\n  static getQueryParamsNames(): any {\n    const qbOptions = RequestQueryBuilder.getOptions();\n    const name = (n) => {\n      const selected = qbOptions.paramNamesMap[n];\n      return isString(selected) ? selected : selected[0];\n    };\n\n    return {\n      delim: qbOptions.delim,\n      delimStr: qbOptions.delimStr,\n      fields: name('fields'),\n      search: name('search'),\n      filter: name('filter'),\n      or: name('or'),\n      join: name('join'),\n      sort: name('sort'),\n      limit: name('limit'),\n      offset: name('offset'),\n      page: name('page'),\n      cache: name('cache'),\n      includeDeleted: name('includeDeleted'),\n    };\n  }\n\n  private static getSwaggerVersion(): number {\n    return swaggerPkgJson ? parseInt(swaggerPkgJson.version[0], 10) : /* istanbul ignore next */ 3;\n  }\n}\n\n// tslint:disable-next-line:ban-types\nexport function ApiProperty(options?: any): PropertyDecorator {\n  return (target: unknown, propertyKey: string | symbol) => {\n    /* istanbul ignore else */\n    if (swagger) {\n      // tslint:disable-next-line\n      const ApiPropertyDecorator = swagger.ApiProperty || /* istanbul ignore next */ swagger.ApiModelProperty;\n      // tslint:disable-next-line\n      ApiPropertyDecorator(options)(target, propertyKey);\n    }\n  };\n}\n"
  },
  {
    "path": "packages/crud/src/crud/validation.helper.ts",
    "content": "import { ValidationPipe } from '@nestjs/common';\nimport { isFalse, isNil } from '@nestjsx/util';\nimport { CrudValidationGroups } from '../enums';\nimport { CreateManyDto, CrudOptions, MergedCrudOptions } from '../interfaces';\nimport { safeRequire } from '../util';\nimport { ApiProperty } from './swagger.helper';\n\nconst validator = safeRequire('class-validator', () => require('class-validator'));\nconst transformer = safeRequire('class-transformer', () => require('class-transformer'));\n\nclass BulkDto<T> implements CreateManyDto<T> {\n  bulk: T[];\n}\n\nexport class Validation {\n  static getValidationPipe(options: CrudOptions, group?: CrudValidationGroups): ValidationPipe {\n    return validator && !isFalse(options.validation)\n      ? new ValidationPipe({\n          ...(options.validation || {}),\n          groups: group ? [group] : undefined,\n        })\n      : /* istanbul ignore next */ undefined;\n  }\n\n  static createBulkDto<T = any>(options: MergedCrudOptions): any {\n    /* istanbul ignore else */\n    if (validator && transformer && !isFalse(options.validation)) {\n      const { IsArray, ArrayNotEmpty, ValidateNested } = validator;\n      const { Type } = transformer;\n      const hasDto = !isNil(options.dto.create);\n      const groups = !hasDto ? [CrudValidationGroups.CREATE] : undefined;\n      const always = hasDto ? true : undefined;\n      const Model = hasDto ? options.dto.create : options.model.type;\n\n      // tslint:disable-next-line:max-classes-per-file\n      class BulkDtoImpl implements CreateManyDto<T> {\n        @ApiProperty({ type: Model, isArray: true })\n        @IsArray({ groups, always })\n        @ArrayNotEmpty({ groups, always })\n        @ValidateNested({ each: true, groups, always })\n        @Type(() => Model)\n        bulk: T[];\n      }\n\n      Object.defineProperty(BulkDtoImpl, 'name', {\n        writable: false,\n        value: `CreateMany${options.model.type.name}Dto`,\n      });\n\n      return BulkDtoImpl;\n    } else {\n      return BulkDto;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/decorators/crud-auth.decorator.ts",
    "content": "import { R } from '../crud/reflection.helper';\nimport { AuthOptions } from '../interfaces';\n\nexport const CrudAuth =\n  (options: AuthOptions) =>\n  (target: unknown): void => {\n    R.setCrudAuthOptions(options, target);\n  };\n"
  },
  {
    "path": "packages/crud/src/decorators/crud.decorator.ts",
    "content": "import { CrudRoutesFactory } from '../crud';\nimport { CrudOptions } from '../interfaces';\n\nexport const Crud =\n  (options: CrudOptions) =>\n  (target: unknown): void => {\n    const factoryMethod = options.routesFactory || CrudRoutesFactory;\n    const factory = new factoryMethod(target, options);\n  };\n"
  },
  {
    "path": "packages/crud/src/decorators/feature-action.decorator.ts",
    "content": "import { SetMetadata, Type } from '@nestjs/common';\n\nimport { ACTION_NAME_METADATA, FEAUTURE_NAME_METADATA } from '../constants';\n\nexport const Feature = (name: string) => SetMetadata(FEAUTURE_NAME_METADATA, name);\nexport const Action = (name: string) => SetMetadata(ACTION_NAME_METADATA, name);\n\nexport const getFeature = <T = any>(target: Type<T>) => Reflect.getMetadata(FEAUTURE_NAME_METADATA, target);\nexport const getAction = (target: unknown) => Reflect.getMetadata(ACTION_NAME_METADATA, target);\n"
  },
  {
    "path": "packages/crud/src/decorators/index.ts",
    "content": "export * from './crud.decorator';\nexport * from './crud-auth.decorator';\nexport * from './override.decorator';\nexport * from './parsed-request.decorator';\nexport * from './parsed-body.decorator';\nexport * from './feature-action.decorator';\n"
  },
  {
    "path": "packages/crud/src/decorators/override.decorator.ts",
    "content": "import { BaseRouteName } from '../types/base-route-name.type';\nimport { OVERRIDE_METHOD_METADATA } from '../constants';\n\nexport const Override = (name?: BaseRouteName) => (\n  target,\n  key,\n  descriptor: PropertyDescriptor,\n) => {\n  Reflect.defineMetadata(OVERRIDE_METHOD_METADATA, name || `${key}Base`, target[key]);\n  return descriptor;\n};\n"
  },
  {
    "path": "packages/crud/src/decorators/parsed-body.decorator.ts",
    "content": "import { PARSED_BODY_METADATA } from '../constants';\n\nexport const ParsedBody = () => (target, key, index) => {\n  Reflect.defineMetadata(PARSED_BODY_METADATA, { index }, target[key]);\n};\n"
  },
  {
    "path": "packages/crud/src/decorators/parsed-request.decorator.ts",
    "content": "import { createParamDecorator } from '@nestjs/common';\n\nimport { PARSED_CRUD_REQUEST_KEY } from '../constants';\nimport { R } from '../crud/reflection.helper';\n\nexport const ParsedRequest = createParamDecorator(\n  (_, ctx): ParameterDecorator => {\n    return R.getContextRequest(ctx)[PARSED_CRUD_REQUEST_KEY];\n  },\n);\n"
  },
  {
    "path": "packages/crud/src/enums/crud-actions.enum.ts",
    "content": "export enum CrudActions {\n  ReadAll = 'Read-All',\n  ReadOne = 'Read-One',\n  CreateOne = 'Create-One',\n  CreateMany = 'Create-Many',\n  UpdateOne = 'Update-One',\n  ReplaceOne = 'Replace-One',\n  DeleteOne = 'Delete-One',\n  DeleteAll = 'Delete-All',\n  RecoverOne = 'Recover-One',\n}\n"
  },
  {
    "path": "packages/crud/src/enums/crud-validation-groups.enum.ts",
    "content": "export enum CrudValidationGroups {\n  CREATE = 'CRUD-CREATE',\n  UPDATE = 'CRUD-UPDATE',\n}\n"
  },
  {
    "path": "packages/crud/src/enums/index.ts",
    "content": "export * from './crud-actions.enum';\nexport * from './crud-validation-groups.enum';\n"
  },
  {
    "path": "packages/crud/src/index.ts",
    "content": "export * from './crud/crud-routes.factory';\nexport * from './decorators';\nexport * from './enums';\nexport * from './interfaces';\nexport * from './types';\nexport * from './module';\nexport * from './interceptors';\nexport * from './services';\n"
  },
  {
    "path": "packages/crud/src/interceptors/crud-base.interceptor.ts",
    "content": "import { ExecutionContext } from '@nestjs/common';\nimport { R } from '../crud/reflection.helper';\nimport { CrudActions } from '../enums';\nimport { MergedCrudOptions } from '../interfaces';\n\nexport class CrudBaseInterceptor {\n  protected getCrudInfo(\n    context: ExecutionContext,\n  ): {\n    ctrlOptions: MergedCrudOptions;\n    crudOptions: Partial<MergedCrudOptions>;\n    action: CrudActions;\n  } {\n    const ctrl = context.getClass();\n    const handler = context.getHandler();\n    const ctrlOptions = R.getCrudOptions(ctrl);\n    const crudOptions = ctrlOptions\n      ? ctrlOptions\n      : {\n          query: {},\n          routes: {},\n          params: {},\n        };\n    const action = R.getAction(handler);\n\n    return { ctrlOptions, crudOptions, action };\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/interceptors/crud-request.interceptor.ts",
    "content": "import { BadRequestException, CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';\nimport { RequestQueryException, RequestQueryParser, SCondition, QueryFilter } from '@nestjsx/crud-request';\nimport { isNil, isFunction, isArrayFull, hasLength } from '@nestjsx/util';\nimport { ClassTransformOptions } from 'class-transformer';\n\nimport { PARSED_CRUD_REQUEST_KEY } from '../constants';\nimport { CrudActions } from '../enums';\nimport { MergedCrudOptions, CrudRequest } from '../interfaces';\nimport { QueryFilterFunction } from '../types';\nimport { CrudBaseInterceptor } from './crud-base.interceptor';\n\n@Injectable()\nexport class CrudRequestInterceptor extends CrudBaseInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler) {\n    const req = context.switchToHttp().getRequest();\n\n    try {\n      /* istanbul ignore else */\n      if (!req[PARSED_CRUD_REQUEST_KEY]) {\n        const { ctrlOptions, crudOptions, action } = this.getCrudInfo(context);\n        const parser = RequestQueryParser.create();\n\n        parser.parseQuery(req.query);\n\n        if (!isNil(ctrlOptions)) {\n          const search = this.getSearch(parser, crudOptions, action, req.params);\n          const auth = this.getAuth(parser, crudOptions, req);\n          parser.search = auth.or ? { $or: [auth.or, { $and: search }] } : { $and: [auth.filter, ...search] };\n        } else {\n          parser.search = { $and: this.getSearch(parser, crudOptions, action) };\n        }\n\n        req[PARSED_CRUD_REQUEST_KEY] = this.getCrudRequest(parser, crudOptions);\n      }\n\n      return next.handle();\n    } catch (error) {\n      /* istanbul ignore next */\n      throw error instanceof RequestQueryException ? new BadRequestException(error.message) : error;\n    }\n  }\n\n  getCrudRequest(parser: RequestQueryParser, crudOptions: Partial<MergedCrudOptions>): CrudRequest {\n    const parsed = parser.getParsed();\n    const { query, routes, params } = crudOptions;\n\n    return {\n      parsed,\n      options: {\n        query,\n        routes,\n        params,\n      },\n    };\n  }\n\n  getSearch(\n    parser: RequestQueryParser,\n    crudOptions: Partial<MergedCrudOptions>,\n    action: CrudActions,\n    params?: any,\n  ): SCondition[] {\n    // params condition\n    const paramsSearch = this.getParamsSearch(parser, crudOptions, params);\n\n    // if `CrudOptions.query.filter` is a function then return transformed query search conditions\n    if (isFunction(crudOptions.query.filter)) {\n      const filterCond =\n        (crudOptions.query.filter as QueryFilterFunction)(parser.search, action === CrudActions.ReadAll) ||\n        /* istanbul ignore next */ {};\n\n      return [...paramsSearch, filterCond];\n    }\n\n    // if `CrudOptions.query.filter` is array or search condition type\n    const optionsFilter = isArrayFull(crudOptions.query.filter)\n      ? (crudOptions.query.filter as QueryFilter[]).map(parser.convertFilterToSearch)\n      : [(crudOptions.query.filter as SCondition) || {}];\n\n    let search: SCondition[] = [];\n\n    if (parser.search) {\n      search = [parser.search];\n    } else if (hasLength(parser.filter) && hasLength(parser.or)) {\n      search =\n        parser.filter.length === 1 && parser.or.length === 1\n          ? [\n              {\n                $or: [parser.convertFilterToSearch(parser.filter[0]), parser.convertFilterToSearch(parser.or[0])],\n              },\n            ]\n          : [\n              {\n                $or: [\n                  { $and: parser.filter.map(parser.convertFilterToSearch) },\n                  { $and: parser.or.map(parser.convertFilterToSearch) },\n                ],\n              },\n            ];\n    } else if (hasLength(parser.filter)) {\n      search = parser.filter.map(parser.convertFilterToSearch);\n    } else {\n      if (hasLength(parser.or)) {\n        search =\n          parser.or.length === 1\n            ? [parser.convertFilterToSearch(parser.or[0])]\n            : /* istanbul ignore next */ [\n                {\n                  $or: parser.or.map(parser.convertFilterToSearch),\n                },\n              ];\n      }\n    }\n\n    return [...paramsSearch, ...optionsFilter, ...search];\n  }\n\n  getParamsSearch(parser: RequestQueryParser, crudOptions: Partial<MergedCrudOptions>, params?: any): SCondition[] {\n    if (params) {\n      parser.parseParams(params, crudOptions.params);\n\n      return isArrayFull(parser.paramsFilter) ? parser.paramsFilter.map(parser.convertFilterToSearch) : [];\n    }\n\n    return [];\n  }\n\n  getAuth(parser: RequestQueryParser, crudOptions: Partial<MergedCrudOptions>, req: any): { filter?: any; or?: any } {\n    const auth: any = {};\n\n    /* istanbul ignore else */\n    if (crudOptions.auth) {\n      const userOrRequest = crudOptions.auth.property ? req[crudOptions.auth.property] : req;\n\n      if (isFunction(crudOptions.auth.or)) {\n        auth.or = crudOptions.auth.or(userOrRequest);\n      }\n\n      if (isFunction(crudOptions.auth.filter) && !auth.or) {\n        auth.filter = crudOptions.auth.filter(userOrRequest) || /* istanbul ignore next */ {};\n      }\n\n      if (isFunction(crudOptions.auth.persist)) {\n        parser.setAuthPersist(crudOptions.auth.persist(userOrRequest));\n      }\n\n      const options: ClassTransformOptions = {};\n      if (isFunction(crudOptions.auth.classTransformOptions)) {\n        Object.assign(options, crudOptions.auth.classTransformOptions(userOrRequest));\n      }\n\n      if (isFunction(crudOptions.auth.groups)) {\n        options.groups = crudOptions.auth.groups(userOrRequest);\n      }\n      parser.setClassTransformOptions(options);\n    }\n\n    return auth;\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/interceptors/crud-response.interceptor.ts",
    "content": "import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';\nimport { isFalse, isObject, isFunction } from '@nestjsx/util';\nimport { classToPlain, classToPlainFromExist, ClassTransformOptions } from 'class-transformer';\nimport { Observable } from 'rxjs';\nimport { map } from 'rxjs/operators';\nimport { CrudActions } from '../enums';\nimport { SerializeOptions } from '../interfaces';\nimport { CrudBaseInterceptor } from './crud-base.interceptor';\n\nconst actionToDtoNameMap: {\n  [key in CrudActions]: keyof SerializeOptions;\n} = {\n  [CrudActions.ReadAll]: 'getMany',\n  [CrudActions.ReadOne]: 'get',\n  [CrudActions.CreateMany]: 'createMany',\n  [CrudActions.CreateOne]: 'create',\n  [CrudActions.UpdateOne]: 'update',\n  [CrudActions.ReplaceOne]: 'replace',\n  [CrudActions.DeleteAll]: 'delete',\n  [CrudActions.DeleteOne]: 'delete',\n  [CrudActions.RecoverOne]: 'recover',\n};\n\n@Injectable()\nexport class CrudResponseInterceptor extends CrudBaseInterceptor implements NestInterceptor {\n  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n    return next.handle().pipe(map((data) => this.serialize(context, data)));\n  }\n\n  protected transform(dto: any, data: any, options: ClassTransformOptions) {\n    if (!isObject(data) || isFalse(dto)) {\n      return data;\n    }\n\n    if (!isFunction(dto)) {\n      return data.constructor !== Object ? classToPlain(data, options) : data;\n    }\n\n    return data instanceof dto\n      ? classToPlain(data, options)\n      : classToPlain(classToPlainFromExist(data, new dto()), options);\n  }\n\n  protected serialize(context: ExecutionContext, data: any): any {\n    const req = context.switchToHttp().getRequest();\n    const { crudOptions, action } = this.getCrudInfo(context);\n    const { serialize } = crudOptions;\n    const dto = serialize[actionToDtoNameMap[action]];\n    const isArray = Array.isArray(data);\n\n    const options: ClassTransformOptions = {};\n    /* istanbul ignore else */\n    if (isFunction(crudOptions.auth?.classTransformOptions)) {\n      const userOrRequest = crudOptions.auth.property ? req[crudOptions.auth.property] : req;\n      Object.assign(options, crudOptions.auth.classTransformOptions(userOrRequest));\n    }\n\n    /* istanbul ignore else */\n    if (isFunction(crudOptions.auth?.groups)) {\n      const userOrRequest = crudOptions.auth.property ? req[crudOptions.auth.property] : req;\n      options.groups = crudOptions.auth.groups(userOrRequest);\n    }\n\n    switch (action) {\n      case CrudActions.ReadAll:\n        return isArray\n          ? (data as any[]).map((item) => this.transform(serialize.get, item, options))\n          : this.transform(dto, data, options);\n      case CrudActions.CreateMany:\n        return isArray\n          ? (data as any[]).map((item) => this.transform(dto, item, options))\n          : this.transform(dto, data, options);\n      default:\n        return this.transform(dto, data, options);\n    }\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/interceptors/index.ts",
    "content": "export * from './crud-request.interceptor';\nexport * from './crud-response.interceptor';\n"
  },
  {
    "path": "packages/crud/src/interfaces/auth-options.interface.ts",
    "content": "import { SCondition } from '@nestjsx/crud-request/lib/types/request-query.types';\nimport { ObjectLiteral } from '@nestjsx/util';\nimport { ClassTransformOptions } from 'class-transformer';\n\nexport interface AuthGlobalOptions {\n  property?: string;\n  /** Get options for the `classToPlain` function (response) */\n  classTransformOptions?: (req: any) => ClassTransformOptions;\n  /** Get `groups` value for the `classToPlain` function options (response) */\n  groups?: (req: any) => string[];\n}\n\nexport interface AuthOptions {\n  property?: string;\n  /** Get options for the `classToPlain` function (response) */\n  classTransformOptions?: (req: any) => ClassTransformOptions;\n  /** Get `groups` value for the `classToPlain` function options (response) */\n  groups?: (req: any) => string[];\n  filter?: (req: any) => SCondition | void;\n  or?: (req: any) => SCondition | void;\n  persist?: (req: any) => ObjectLiteral;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/base-route.interface.ts",
    "content": "import { RequestMethod } from '@nestjs/common';\n\nimport { BaseRouteName } from '../types';\n\nexport interface BaseRoute {\n  name: BaseRouteName;\n  path: string;\n  method: RequestMethod;\n  enable: boolean;\n  override: boolean;\n  withParams: boolean;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/create-many-dto.interface.ts",
    "content": "export interface CreateManyDto<T = any> {\n  bulk: T[];\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/crud-controller.interface.ts",
    "content": "import { CrudService } from '../services';\nimport { CrudRequest, GetManyDefaultResponse, CreateManyDto } from '../interfaces';\n\nexport interface CrudController<T> {\n  service: CrudService<T>;\n  getManyBase?(req: CrudRequest): Promise<GetManyDefaultResponse<T> | T[]>;\n  getOneBase?(req: CrudRequest): Promise<T>;\n  createOneBase?(req: CrudRequest, dto: T): Promise<T>;\n  createManyBase?(req: CrudRequest, dto: CreateManyDto<T>): Promise<T[]>;\n  updateOneBase?(req: CrudRequest, dto: T): Promise<T>;\n  replaceOneBase?(req: CrudRequest, dto: T): Promise<T>;\n  deleteOneBase?(req: CrudRequest): Promise<void | T>;\n  recoverOneBase?(req: CrudRequest): Promise<void | T>;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/crud-global-config.interface.ts",
    "content": "import { RequestQueryBuilderOptions } from '@nestjsx/crud-request';\n\nimport { RoutesOptions } from './routes-options.interface';\nimport { ParamsOptions } from './params-options.interface';\nimport { AuthGlobalOptions } from './auth-options.interface';\n\nexport interface CrudGlobalConfig {\n  queryParser?: RequestQueryBuilderOptions;\n  auth?: AuthGlobalOptions;\n  routes?: RoutesOptions;\n  params?: ParamsOptions;\n  query?: {\n    limit?: number;\n    maxLimit?: number;\n    cache?: number | false;\n    alwaysPaginate?: boolean;\n    softDelete?: boolean;\n  };\n  serialize?: {\n    getMany?: false;\n    get?: false;\n    create?: false;\n    createMany?: false;\n    update?: false;\n    replace?: false;\n    delete?: false;\n    recover?: false;\n  };\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/crud-options.interface.ts",
    "content": "import { ValidationPipeOptions } from '@nestjs/common';\n\nimport { CrudRoutesFactory } from '../crud';\nimport { ModelOptions } from './model-options.interface';\nimport { ParamsOptions } from './params-options.interface';\nimport { QueryOptions } from './query-options.interface';\nimport { RoutesOptions } from './routes-options.interface';\nimport { AuthOptions } from './auth-options.interface';\nimport { DtoOptions } from './dto-options.interface';\nimport { SerializeOptions } from './serialize-options.interface';\n\nexport interface CrudRequestOptions {\n  query?: QueryOptions;\n  routes?: RoutesOptions;\n  params?: ParamsOptions;\n}\n\nexport interface CrudOptions {\n  model: ModelOptions;\n  dto?: DtoOptions;\n  serialize?: SerializeOptions;\n  query?: QueryOptions;\n  routes?: RoutesOptions;\n  routesFactory?: typeof CrudRoutesFactory;\n  params?: ParamsOptions;\n  validation?: ValidationPipeOptions | false;\n}\n\nexport interface MergedCrudOptions extends CrudOptions {\n  auth?: AuthOptions;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/crud-request.interface.ts",
    "content": "import { ParsedRequestParams } from '@nestjsx/crud-request';\n\nimport { CrudRequestOptions } from '../interfaces';\n\nexport interface CrudRequest {\n  parsed: ParsedRequestParams;\n  options: CrudRequestOptions;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/dto-options.interface.ts",
    "content": "export interface DtoOptions {\n  create?: any;\n  update?: any;\n  replace?: any;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/get-many-default-response.interface.ts",
    "content": "export interface GetManyDefaultResponse<T> {\n  data: T[];\n  count: number;\n  total: number;\n  page: number;\n  pageCount: number;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/index.ts",
    "content": "export * from './crud-controller.interface';\nexport * from './crud-options.interface';\nexport * from './auth-options.interface';\nexport * from './params-options.interface';\nexport * from './query-options.interface';\nexport * from './routes-options.interface';\nexport * from './base-route.interface';\nexport * from './crud-request.interface';\nexport * from './model-options.interface';\nexport * from './create-many-dto.interface';\nexport * from './get-many-default-response.interface';\nexport * from './crud-global-config.interface';\nexport * from './dto-options.interface';\nexport * from './serialize-options.interface';\n"
  },
  {
    "path": "packages/crud/src/interfaces/model-options.interface.ts",
    "content": "export interface ModelOptions {\n  type: any;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/params-options.interface.ts",
    "content": "import { SwaggerEnumType } from '@nestjs/swagger/dist/types/swagger-enum.type';\nimport { ParamOptionType } from '@nestjsx/crud-request';\n\nexport interface ParamsOptions {\n  [key: string]: ParamOption;\n}\n\nexport interface ParamOption {\n  field?: string;\n  type?: ParamOptionType;\n  enum?: SwaggerEnumType;\n  primary?: boolean;\n  disabled?: boolean;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/query-options.interface.ts",
    "content": "import {\n  QueryFields,\n  QuerySort,\n} from '@nestjsx/crud-request/lib/types/request-query.types';\n\nimport { QueryFilterOption } from '../types';\n\nexport interface QueryOptions {\n  allow?: QueryFields;\n  exclude?: QueryFields;\n  persist?: QueryFields;\n  filter?: QueryFilterOption;\n  join?: JoinOptions;\n  sort?: QuerySort[];\n  limit?: number;\n  maxLimit?: number;\n  cache?: number | false;\n  alwaysPaginate?: boolean;\n  softDelete?: boolean;\n}\n\nexport interface JoinOptions {\n  [key: string]: JoinOption;\n}\n\nexport interface JoinOption {\n  alias?: string;\n  allow?: QueryFields;\n  eager?: boolean;\n  exclude?: QueryFields;\n  persist?: QueryFields;\n  select?: false;\n  required?: boolean;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/routes-options.interface.ts",
    "content": "import { BaseRouteName } from '../types';\n\nexport interface RoutesOptions {\n  exclude?: BaseRouteName[];\n  only?: BaseRouteName[];\n  getManyBase?: GetManyRouteOptions;\n  getOneBase?: GetOneRouteOptions;\n  createOneBase?: CreateOneRouteOptions;\n  createManyBase?: CreateManyRouteOptions;\n  updateOneBase?: UpdateOneRouteOptions;\n  replaceOneBase?: ReplaceOneRouteOptions;\n  deleteOneBase?: DeleteOneRouteOptions;\n  recoverOneBase?: RecoverOneRouteOptions;\n}\n\nexport interface BaseRouteOptions {\n  interceptors?: any[];\n  decorators?: (PropertyDecorator | MethodDecorator)[];\n}\n\nexport type GetManyRouteOptions = BaseRouteOptions;\n\nexport type GetOneRouteOptions = BaseRouteOptions;\n\nexport interface CreateOneRouteOptions extends BaseRouteOptions {\n  returnShallow?: boolean;\n}\n\nexport type CreateManyRouteOptions = BaseRouteOptions;\n\nexport interface ReplaceOneRouteOptions extends BaseRouteOptions {\n  allowParamsOverride?: boolean;\n  returnShallow?: boolean;\n}\n\nexport interface UpdateOneRouteOptions extends BaseRouteOptions {\n  allowParamsOverride?: boolean;\n  returnShallow?: boolean;\n}\n\nexport interface DeleteOneRouteOptions extends BaseRouteOptions {\n  returnDeleted?: boolean;\n}\n\nexport interface RecoverOneRouteOptions extends BaseRouteOptions {\n  returnRecovered?: boolean;\n}\n"
  },
  {
    "path": "packages/crud/src/interfaces/serialize-options.interface.ts",
    "content": "import { Type } from '@nestjs/common';\n\nexport interface SerializeOptions {\n  getMany?: Type<any> | false;\n  get?: Type<any> | false;\n  create?: Type<any> | false;\n  createMany?: Type<any> | false;\n  update?: Type<any> | false;\n  replace?: Type<any> | false;\n  delete?: Type<any> | false;\n  recover?: Type<any> | false;\n}\n"
  },
  {
    "path": "packages/crud/src/module/crud-config.service.ts",
    "content": "import { RequestQueryBuilder } from '@nestjsx/crud-request';\nimport { isObjectFull } from '@nestjsx/util';\nimport * as deepmerge from 'deepmerge';\n\nimport { CrudGlobalConfig } from '../interfaces';\n\nexport class CrudConfigService {\n  static config: CrudGlobalConfig = {\n    auth: {},\n    query: {\n      alwaysPaginate: false,\n    },\n    routes: {\n      getManyBase: { interceptors: [], decorators: [] },\n      getOneBase: { interceptors: [], decorators: [] },\n      createOneBase: { interceptors: [], decorators: [], returnShallow: false },\n      createManyBase: { interceptors: [], decorators: [] },\n      updateOneBase: {\n        interceptors: [],\n        decorators: [],\n        allowParamsOverride: false,\n        returnShallow: false,\n      },\n      replaceOneBase: {\n        interceptors: [],\n        decorators: [],\n        allowParamsOverride: false,\n        returnShallow: false,\n      },\n      deleteOneBase: { interceptors: [], decorators: [], returnDeleted: false },\n      recoverOneBase: { interceptors: [], decorators: [], returnRecovered: false },\n    },\n    params: {},\n  };\n\n  static load(config: CrudGlobalConfig = {}) {\n    if (isObjectFull(config.queryParser)) {\n      RequestQueryBuilder.setOptions(config.queryParser);\n    }\n\n    const auth = isObjectFull(config.auth) ? config.auth : {};\n    const query = isObjectFull(config.query) ? config.query : {};\n    const routes = isObjectFull(config.routes) ? config.routes : {};\n    const params = isObjectFull(config.params) ? config.params : {};\n    const serialize = isObjectFull(config.serialize) ? config.serialize : {};\n\n    CrudConfigService.config = deepmerge(\n      CrudConfigService.config,\n      { auth, query, routes, params, serialize },\n      { arrayMerge: (a, b, c) => b },\n    );\n  }\n}\n"
  },
  {
    "path": "packages/crud/src/module/index.ts",
    "content": "export * from './crud-config.service';\n"
  },
  {
    "path": "packages/crud/src/services/crud-service.abstract.ts",
    "content": "import { BadRequestException, NotFoundException } from '@nestjs/common';\nimport { ParsedRequestParams } from '@nestjsx/crud-request';\nimport { objKeys } from '@nestjsx/util';\n\nimport { CreateManyDto, CrudRequest, CrudRequestOptions, GetManyDefaultResponse, QueryOptions } from '../interfaces';\n\nexport abstract class CrudService<T> {\n  throwBadRequestException(msg?: unknown): BadRequestException {\n    throw new BadRequestException(msg);\n  }\n\n  throwNotFoundException(name: string): NotFoundException {\n    throw new NotFoundException(`${name} not found`);\n  }\n\n  /**\n   * Wrap page into page-info\n   * override this method to create custom page-info response\n   * or set custom `serialize.getMany` dto in the controller's CrudOption\n   * @param data\n   * @param total\n   * @param limit\n   * @param offset\n   */\n  createPageInfo(data: T[], total: number, limit: number, offset: number): GetManyDefaultResponse<T> {\n    return {\n      data,\n      count: data.length,\n      total,\n      page: limit ? Math.floor(offset / limit) + 1 : 1,\n      pageCount: limit && total ? Math.ceil(total / limit) : 1,\n    };\n  }\n\n  /**\n   * Determine if need paging\n   * @param parsed\n   * @param options\n   */\n  decidePagination(parsed: ParsedRequestParams, options: CrudRequestOptions): boolean {\n    return (\n      options.query.alwaysPaginate ||\n      ((Number.isFinite(parsed.page) || Number.isFinite(parsed.offset)) &&\n        /* istanbul ignore next */ !!this.getTake(parsed, options.query))\n    );\n  }\n\n  /**\n   * Get number of resources to be fetched\n   * @param query\n   * @param options\n   */\n  getTake(query: ParsedRequestParams, options: QueryOptions): number | null {\n    if (query.limit) {\n      return options.maxLimit ? (query.limit <= options.maxLimit ? query.limit : options.maxLimit) : query.limit;\n    }\n    /* istanbul ignore if */\n    if (options.limit) {\n      return options.maxLimit ? (options.limit <= options.maxLimit ? options.limit : options.maxLimit) : options.limit;\n    }\n\n    return options.maxLimit ? options.maxLimit : null;\n  }\n\n  /**\n   * Get number of resources to be skipped\n   * @param query\n   * @param take\n   */\n  getSkip(query: ParsedRequestParams, take: number): number | null {\n    return query.page && take ? take * (query.page - 1) : query.offset ? query.offset : null;\n  }\n\n  /**\n   * Get primary param name from CrudOptions\n   * @param options\n   */\n  getPrimaryParams(options: CrudRequestOptions): string[] {\n    const params = objKeys(options.params).filter((n) => options.params[n] && options.params[n].primary);\n\n    return params.map((p) => options.params[p].field);\n  }\n\n  abstract getMany(req: CrudRequest): Promise<GetManyDefaultResponse<T> | T[]>;\n\n  abstract getOne(req: CrudRequest): Promise<T>;\n\n  abstract createOne(req: CrudRequest, dto: T | Partial<T>): Promise<T>;\n\n  abstract createMany(req: CrudRequest, dto: CreateManyDto): Promise<T[]>;\n\n  abstract updateOne(req: CrudRequest, dto: T | Partial<T>): Promise<T>;\n\n  abstract replaceOne(req: CrudRequest, dto: T | Partial<T>): Promise<T>;\n\n  abstract deleteOne(req: CrudRequest): Promise<void | T>;\n\n  abstract recoverOne(req: CrudRequest): Promise<void | T>;\n}\n"
  },
  {
    "path": "packages/crud/src/services/index.ts",
    "content": "export * from './crud-service.abstract';\n"
  },
  {
    "path": "packages/crud/src/types/base-route-name.type.ts",
    "content": "export type BaseRouteName =\n  | 'getManyBase'\n  | 'getOneBase'\n  | 'createOneBase'\n  | 'createManyBase'\n  | 'updateOneBase'\n  | 'replaceOneBase'\n  | 'deleteOneBase'\n  | 'recoverOneBase';\n"
  },
  {
    "path": "packages/crud/src/types/index.ts",
    "content": "export * from './base-route-name.type';\nexport * from './query-filter-option.type';\n"
  },
  {
    "path": "packages/crud/src/types/query-filter-option.type.ts",
    "content": "import {\n  QueryFilter,\n  SCondition,\n} from '@nestjsx/crud-request/lib/types/request-query.types';\n\nexport type QueryFilterFunction = (\n  search?: SCondition,\n  getMany?: boolean,\n) => SCondition | void;\nexport type QueryFilterOption = QueryFilter[] | SCondition | QueryFilterFunction;\n"
  },
  {
    "path": "packages/crud/src/util.ts",
    "content": "export function safeRequire<T = any>(path: string, loader?: () => T): T | null {\n  try {\n    /* istanbul ignore next */\n    const pack = loader ? loader() : require(path);\n    return pack;\n  } catch (_) {\n    /* istanbul ignore next */\n    return null;\n  }\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/dto/index.ts",
    "content": "export * from './test-create.dto';\nexport * from './test-update.dto';\n"
  },
  {
    "path": "packages/crud/test/__fixture__/dto/test-create.dto.ts",
    "content": "import {\n  IsString,\n  IsEmail,\n  IsNumber,\n  IsOptional,\n  IsNotEmpty,\n  IsEmpty,\n} from 'class-validator';\n\nexport class TestCreateDto {\n  @IsString()\n  firstName: string;\n\n  @IsString()\n  lastName: string;\n\n  @IsEmail({ require_tld: false })\n  email: string;\n\n  @IsNumber()\n  age: number;\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/dto/test-update.dto.ts",
    "content": "import {\n  IsString,\n  IsEmail,\n  IsNumber,\n  IsOptional,\n  IsNotEmpty,\n  IsEmpty,\n} from 'class-validator';\n\nexport class TestUpdateDto {\n  @IsOptional()\n  @IsString()\n  firstName?: string;\n\n  @IsOptional()\n  @IsString()\n  lastName?: string;\n\n  @IsOptional()\n  @IsEmail({ require_tld: false })\n  email?: string;\n\n  @IsOptional()\n  @IsNumber()\n  age?: number;\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/exception.filter.ts",
    "content": "import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common';\nimport { RequestQueryException } from '@nestjsx/crud-request';\nimport { Response } from 'express';\n\n@Catch(RequestQueryException)\nexport class HttpExceptionFilter implements ExceptionFilter {\n  catch(exception: RequestQueryException, host: ArgumentsHost) {\n    const ctx = host.switchToHttp();\n    const response = ctx.getResponse<Response>();\n\n    response.status(HttpStatus.BAD_REQUEST).json({\n      statusCode: HttpStatus.BAD_REQUEST,\n      message: exception.message,\n    });\n  }\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/models/index.ts",
    "content": "export * from './test.model';\nexport * from './test-serialize.model';\nexport * from './test-serialize-2.model';\n"
  },
  {
    "path": "packages/crud/test/__fixture__/models/test-serialize-2.model.ts",
    "content": "import { Exclude } from 'class-transformer';\n\nimport { TestSerializeModel } from './test-serialize.model';\n\nexport class TestSerialize2Model extends TestSerializeModel {\n  id: number;\n\n  name: string;\n\n  email: string;\n\n  @Exclude()\n  isActive: boolean;\n\n  constructor(partial: Partial<TestSerialize2Model>) {\n    super(partial);\n    Object.assign(this, partial);\n  }\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/models/test-serialize.model.ts",
    "content": "export class TestSerializeModel {\n  id: number;\n\n  name: string;\n\n  email: string;\n\n  isActive: boolean;\n\n  constructor(partial: Partial<TestSerializeModel>) {\n    Object.assign(this, partial);\n  }\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/models/test.model.ts",
    "content": "import {\n  IsString,\n  IsEmail,\n  IsNumber,\n  IsOptional,\n  IsNotEmpty,\n  IsEmpty,\n} from 'class-validator';\n\nimport { CrudValidationGroups } from '../../../src';\n\nconst { CREATE, UPDATE } = CrudValidationGroups;\n\nexport class TestModel {\n  @IsEmpty({ groups: [CREATE] })\n  @IsNumber({}, { groups: [UPDATE] })\n  id?: number;\n\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @IsString({ always: true })\n  firstName?: string;\n\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @IsString({ always: true })\n  lastName?: string;\n\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @IsEmail({ require_tld: false }, { always: true })\n  email?: string;\n\n  @IsOptional({ groups: [UPDATE] })\n  @IsNotEmpty({ groups: [CREATE] })\n  @IsNumber({}, { always: true })\n  age?: number;\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/response/delete-model-response.dto.ts",
    "content": "import { Exclude, Expose } from 'class-transformer';\n\n@Exclude()\nexport class DeleteModelResponseDto {\n  @Expose()\n  id: number;\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/response/get-many-model-response.dto.ts",
    "content": "import { Type } from 'class-transformer';\n\nimport { GetModelResponseDto } from './get-model-response.dto';\n\nexport class GetManyModelResponseDto {\n  @Type(() => GetModelResponseDto)\n  items: GetModelResponseDto[];\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/response/get-model-response.dto.ts",
    "content": "import { Exclude } from 'class-transformer';\n\nexport class GetModelResponseDto {\n  id: number;\n\n  name: string;\n\n  @Exclude()\n  email: string;\n\n  isActive: boolean;\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/response/index.ts",
    "content": "export * from './get-many-model-response.dto';\nexport * from './get-model-response.dto';\nexport * from './delete-model-response.dto';\nexport * from './recover-model-response.dto';\n"
  },
  {
    "path": "packages/crud/test/__fixture__/response/recover-model-response.dto.ts",
    "content": "import { Exclude } from 'class-transformer';\n\nexport class RecoverModelResponseDto {\n  id: number;\n\n  name: string;\n\n  email: string;\n\n  isActive: boolean;\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/services/index.ts",
    "content": "export * from './test.service';\nexport * from './test-serialize.service';\n"
  },
  {
    "path": "packages/crud/test/__fixture__/services/test-serialize.service.ts",
    "content": "import { Injectable, Type } from '@nestjs/common';\n\nimport { CreateManyDto, CrudRequest, GetManyDefaultResponse } from '../../../src/interfaces';\nimport { CrudService } from '../../../src/services';\nimport { TestSerializeModel } from '../models';\n\n@Injectable()\nexport class TestSerializeService<T = TestSerializeModel> extends CrudService<T> {\n  private store: T[] = [];\n\n  constructor(private Model: Type<T>) {\n    super();\n    this.store = [\n      new this.Model({ id: 1, name: 'name', email: 'email1', isActive: true }),\n      new this.Model({ id: 2, name: 'name2', email: 'email2', isActive: false }),\n      new this.Model({ id: 3, name: 'name3', email: 'email3', isActive: true }),\n      new this.Model({ id: 4, name: 'name4', email: 'email4', isActive: false }),\n      new this.Model({ id: 5, name: 'name5', email: 'email5', isActive: true }),\n    ];\n  }\n\n  async getMany(req: CrudRequest): Promise<GetManyDefaultResponse<T> | T[]> {\n    const total = this.store.length;\n    const limit = this.getTake(req.parsed, req.options.query);\n    const offset = this.getSkip(req.parsed, limit);\n\n    return this.decidePagination(req.parsed, req.options)\n      ? this.createPageInfo(this.store, total, limit || total, offset || 0)\n      : this.store;\n  }\n\n  async getOne(req: CrudRequest): Promise<T> {\n    return this.store[0];\n  }\n\n  async createOne(req: CrudRequest, dto: T): Promise<any> {}\n\n  async createMany(req: CrudRequest, dto: CreateManyDto<T>): Promise<any> {}\n\n  async updateOne(req: CrudRequest, dto: T): Promise<any> {}\n\n  async replaceOne(req: CrudRequest, dto: T): Promise<any> {}\n\n  async deleteOne(req: CrudRequest): Promise<any> {\n    return req.options.routes.deleteOneBase.returnDeleted ? this.store[0] : undefined;\n  }\n\n  async recoverOne(req: CrudRequest): Promise<any> {\n    return req.options.routes.recoverOneBase.returnRecovered ? this.store[0] : undefined;\n  }\n}\n"
  },
  {
    "path": "packages/crud/test/__fixture__/services/test.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { ParsedRequestParams } from '@nestjsx/crud-request';\nimport { CrudRequestOptions } from '../../../src/interfaces';\n\nimport { CreateManyDto, CrudRequest } from '../../../src/interfaces';\nimport { CrudService } from '../../../src/services';\n\n@Injectable()\nexport class TestService<T> extends CrudService<T> {\n  async getMany(req: CrudRequest): Promise<any> {\n    return { req };\n  }\n\n  async getOne(req: CrudRequest): Promise<any> {\n    return { req };\n  }\n\n  async createOne(req: CrudRequest, dto: T): Promise<any> {\n    return { req, dto };\n  }\n\n  async createMany(req: CrudRequest, dto: CreateManyDto<T>): Promise<any> {\n    return { req, dto };\n  }\n\n  async updateOne(req: CrudRequest, dto: T): Promise<any> {\n    return { req, dto };\n  }\n\n  async replaceOne(req: CrudRequest, dto: T): Promise<any> {\n    return { req, dto };\n  }\n\n  async deleteOne(req: CrudRequest): Promise<any> {\n    return { req };\n  }\n\n  async recoverOne(req: CrudRequest): Promise<any> {\n    return { req };\n  }\n\n  decidePagination(parsed: ParsedRequestParams, options: CrudRequestOptions): boolean {\n    return true;\n  }\n}\n"
  },
  {
    "path": "packages/crud/test/crud-config.service.global.spec.ts",
    "content": "import * as request from 'supertest';\nimport { Test } from '@nestjs/testing';\nimport { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\n\nimport { CrudGlobalConfig } from '../src/interfaces';\nimport { CrudConfigService } from '../src/module/crud-config.service';\n\n// IMPORTANT:\n// CrudConfigService.load() should be called before importing @Crud() controllers\n\nconst conf: CrudGlobalConfig = {\n  query: {\n    limit: 10,\n  },\n  params: {\n    id: {\n      field: 'id',\n      type: 'uuid',\n      primary: true,\n    },\n  },\n  routes: {\n    exclude: ['createManyBase'],\n    updateOneBase: {\n      allowParamsOverride: true,\n    },\n    replaceOneBase: {\n      allowParamsOverride: true,\n    },\n  },\n  serialize: {\n    get: false,\n  },\n};\n\n// Important: load config before (!!!) you import AppModule\n// https://github.com/nestjsx/crud/wiki/Controllers#global-options\nCrudConfigService.load(conf);\n\nimport { Crud } from '../src/decorators/crud.decorator';\nimport { HttpExceptionFilter } from './__fixture__/exception.filter';\nimport { TestModel } from './__fixture__/models';\nimport { TestService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#CrudConfigService', () => {\n    let app: INestApplication;\n    let server: any;\n\n    @Crud({\n      model: { type: TestModel },\n    })\n    @Controller('test')\n    class GlobalTestController {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    @Crud({\n      model: { type: TestModel },\n      query: {\n        limit: 12,\n      },\n      params: {\n        id: {\n          field: 'id',\n          type: 'number',\n          primary: true,\n        },\n      },\n      routes: {\n        updateOneBase: {\n          allowParamsOverride: false,\n        },\n        replaceOneBase: {\n          allowParamsOverride: false,\n        },\n        deleteOneBase: {\n          returnDeleted: true,\n        },\n      },\n    })\n    @Controller('test2')\n    class GlobalTestController2 {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [GlobalTestController, GlobalTestController2],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      app.close();\n    });\n\n    it('should use global config', (done) => {\n      request(server)\n        .get('/test')\n        .end((_, res) => {\n          expect(res.status).toBe(200);\n          expect(res.body.req.options.query).toMatchObject(conf.query);\n          expect(res.body.req.options.params).toMatchObject(conf.params);\n          expect(res.body.req.options.routes.updateOneBase.allowParamsOverride).toBe(true);\n          expect(res.body.req.options.routes.replaceOneBase.allowParamsOverride).toBe(true);\n          done();\n        });\n    });\n    it('should use merged config', (done) => {\n      request(server)\n        .get('/test2')\n        .end((_, res) => {\n          expect(res.status).toBe(200);\n          expect(res.body.req.options.query).toMatchObject({\n            limit: 12,\n          });\n          expect(res.body.req.options.params).toMatchObject({\n            id: {\n              field: 'id',\n              type: 'number',\n              primary: true,\n            },\n          });\n          expect(res.body.req.options.routes.updateOneBase.allowParamsOverride).toBe(false);\n          expect(res.body.req.options.routes.replaceOneBase.allowParamsOverride).toBe(false);\n          expect(res.body.req.options.routes.deleteOneBase.returnDeleted).toBe(true);\n          done();\n        });\n    });\n    it('should exclude route, 1', (done) => {\n      request(server)\n        .post('/test/bulk')\n        .send({})\n        .end((_, res) => {\n          expect(res.status).toBe(404);\n          done();\n        });\n    });\n    it('should exclude route, 1', (done) => {\n      request(server)\n        .post('/test2/bulk')\n        .send({})\n        .end((_, res) => {\n          expect(res.status).toBe(404);\n          done();\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud-config.service.spec.ts",
    "content": "import { RequestQueryBuilder } from '@nestjsx/crud-request';\nimport { CrudGlobalConfig } from '../src/interfaces';\nimport { CrudConfigService } from '../src/module/crud-config.service';\n\ndescribe('#crud', () => {\n  describe('#CrudConfigService', () => {\n    const defaultConfig = { ...CrudConfigService.config };\n\n    beforeEach(() => {\n      CrudConfigService.config = { ...defaultConfig };\n    });\n\n    it('should set default config, 1', () => {\n      const conf: CrudGlobalConfig = {};\n      const expected = { ...CrudConfigService.config };\n      CrudConfigService.load(conf);\n      expect(CrudConfigService.config).toEqual(expect.objectContaining(expected));\n    });\n    it('should set default config, 2', () => {\n      const expected = { ...CrudConfigService.config };\n      CrudConfigService.load();\n      expect(CrudConfigService.config).toEqual(expect.objectContaining(expected));\n    });\n    it('should set queryParser', () => {\n      const requestOptions = { ...RequestQueryBuilder.getOptions() };\n      const conf: CrudGlobalConfig = {\n        queryParser: {\n          delim: '__',\n        },\n      };\n      const expected = { ...CrudConfigService.config };\n      CrudConfigService.load(conf);\n      expect(CrudConfigService.config).toEqual(expect.objectContaining(expected));\n      expect(RequestQueryBuilder.getOptions()).toEqual(\n        expect.objectContaining({ ...requestOptions, delim: '__' }),\n      );\n    });\n    it('should set query, routes, params', () => {\n      const conf: CrudGlobalConfig = {\n        auth: {\n          property: 'user',\n        },\n        query: {\n          limit: 10,\n        },\n        params: {\n          id: {\n            field: 'id',\n            type: 'uuid',\n            primary: true,\n          },\n        },\n        routes: {\n          updateOneBase: {\n            allowParamsOverride: true,\n            returnShallow: true,\n          },\n          replaceOneBase: {\n            allowParamsOverride: true,\n          },\n          getManyBase: {\n            interceptors: [() => {}],\n          },\n        },\n      };\n      const expected = {\n        auth: {\n          property: 'user',\n        },\n        query: {\n          limit: 10,\n        },\n        params: {\n          id: {\n            field: 'id',\n            type: 'uuid',\n            primary: true,\n          },\n        },\n        routes: {\n          getManyBase: {\n            interceptors: [() => {}],\n            decorators: [],\n          },\n          getOneBase: { interceptors: [], decorators: [] },\n          createOneBase: { interceptors: [], decorators: [], returnShallow: false },\n          createManyBase: { interceptors: [], decorators: [] },\n          updateOneBase: {\n            interceptors: [],\n            decorators: [],\n            allowParamsOverride: true,\n            returnShallow: true,\n          },\n          replaceOneBase: {\n            interceptors: [],\n            decorators: [],\n            allowParamsOverride: true,\n            returnShallow: false,\n          },\n          deleteOneBase: { interceptors: [], decorators: [], returnDeleted: false },\n          recoverOneBase: { interceptors: [], decorators: [], returnRecovered: false },\n        },\n      };\n      CrudConfigService.load(conf);\n      expect(CrudConfigService.config.params).toEqual(\n        expect.objectContaining(expected.params),\n      );\n      expect(CrudConfigService.config.query).toEqual(\n        expect.objectContaining(expected.query),\n      );\n      expect(JSON.stringify(CrudConfigService.config.routes)).toEqual(\n        JSON.stringify(expected.routes),\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud-request.interceptor.spec.ts",
    "content": "import { Controller, Get, Param, ParseIntPipe, Query, UseInterceptors } from '@nestjs/common';\nimport { NestApplication } from '@nestjs/core';\nimport { Test } from '@nestjs/testing';\nimport { RequestQueryBuilder } from '@nestjsx/crud-request';\nimport * as supertest from 'supertest';\nimport { Crud, ParsedRequest, CrudAuth, Override } from '../src/decorators';\nimport { CrudRequestInterceptor } from '../src/interceptors';\nimport { CrudRequest } from '../src/interfaces';\nimport { TestModel } from './__fixture__/models';\nimport { TestService } from './__fixture__/services';\n\n// tslint:disable:max-classes-per-file\ndescribe('#crud', () => {\n  @UseInterceptors(CrudRequestInterceptor)\n  @Controller('test')\n  class TestController {\n    @Get('/query')\n    async query(@ParsedRequest() req: CrudRequest) {\n      return req;\n    }\n\n    @Get('/other')\n    async other(@Query('page', ParseIntPipe) page: number) {\n      return { page };\n    }\n\n    @Get('/other2/:someParam')\n    async routeWithParam(@Param('someParam', ParseIntPipe) p: number) {\n      return { p };\n    }\n  }\n\n  @Crud({\n    model: { type: TestModel },\n    params: {\n      someParam: { field: 'someParam', type: 'number' },\n    },\n  })\n  @Controller('test2')\n  class Test2Controller {\n    constructor(public service: TestService<TestModel>) {}\n\n    @UseInterceptors(CrudRequestInterceptor)\n    @Get('normal/:id')\n    async normal(@ParsedRequest() req: CrudRequest) {\n      return { filter: req.parsed.paramsFilter };\n    }\n\n    @UseInterceptors(CrudRequestInterceptor)\n    @Get('/other2/:someParam')\n    async routeWithParam(@Param('someParam', ParseIntPipe) p: number) {\n      return { p };\n    }\n\n    @UseInterceptors(CrudRequestInterceptor)\n    @Get('other2/:id/twoParams/:someParam')\n    async twoParams(@ParsedRequest() req: CrudRequest, @Param('someParam', ParseIntPipe) p: number) {\n      return { filter: req.parsed.paramsFilter };\n    }\n  }\n\n  @Crud({\n    model: { type: TestModel },\n    query: {\n      filter: () => ({ name: 'persist' }),\n    },\n  })\n  @CrudAuth({\n    property: 'user',\n    filter: (user) => ({ user: 'test', buz: 1 }),\n    persist: () => ({ bar: false }),\n  })\n  @Controller('test3')\n  class Test3Controller {\n    constructor(public service: TestService<TestModel>) {}\n\n    @Override('getManyBase')\n    get(@ParsedRequest() req: CrudRequest) {\n      return req;\n    }\n\n    @Override('createOneBase')\n    post(@ParsedRequest() req: CrudRequest) {\n      return req;\n    }\n  }\n\n  @Crud({\n    model: { type: TestModel },\n  })\n  @CrudAuth({\n    or: () => ({ id: 1 }),\n  })\n  @Controller('test4')\n  class Test4Controller {\n    constructor(public service: TestService<TestModel>) {}\n\n    @Override('getManyBase')\n    get(@ParsedRequest() req: CrudRequest) {\n      return req;\n    }\n  }\n\n  @Crud({\n    model: { type: TestModel },\n    params: {\n      someParam: { field: 'someParam', type: 'number', primary: true },\n      someParam2: { field: 'someParam2', type: 'number', primary: true },\n    },\n  })\n  @Controller('test5')\n  class Test5Controller {\n    constructor(public service: TestService<TestModel>) {}\n  }\n\n  @Crud({\n    model: { type: TestModel },\n  })\n  @CrudAuth({\n    groups: () => ['TEST_2'],\n    classTransformOptions: () => ({ groups: ['TEST_1'] }),\n  })\n  @Controller('test6')\n  class Test6Controller {\n    constructor(public service: TestService<TestModel>) {}\n\n    @Override('getManyBase')\n    get(@ParsedRequest() req: CrudRequest) {\n      return req;\n    }\n  }\n\n  let $: supertest.SuperTest<supertest.Test>;\n  let app: NestApplication;\n\n  beforeAll(async () => {\n    const module = await Test.createTestingModule({\n      providers: [TestService],\n      controllers: [\n        TestController,\n        Test2Controller,\n        Test3Controller,\n        Test4Controller,\n        Test5Controller,\n        Test6Controller,\n      ],\n    }).compile();\n    app = module.createNestApplication();\n    await app.init();\n\n    $ = supertest(app.getHttpServer());\n  });\n\n  afterAll(async () => {\n    await app.close();\n  });\n\n  describe('#interceptor', () => {\n    let qb: RequestQueryBuilder;\n\n    beforeEach(() => {\n      qb = RequestQueryBuilder.create();\n    });\n\n    it('should working on non-crud controller', async () => {\n      const page = 2;\n      const limit = 10;\n      const fields = ['a', 'b', 'c'];\n      const sorts: any[][] = [\n        ['a', 'ASC'],\n        ['b', 'DESC'],\n      ];\n      const filters: any[][] = [\n        ['a', 'eq', 1],\n        ['c', 'in', [1, 2, 3]],\n        ['d', 'notnull'],\n      ];\n\n      qb.setPage(page).setLimit(limit);\n      qb.select(fields);\n      for (const s of sorts) {\n        qb.sortBy({ field: s[0], order: s[1] });\n      }\n      for (const f of filters) {\n        qb.setFilter({ field: f[0], operator: f[1], value: f[2] });\n      }\n\n      const res = await $.get('/test/query').query(qb.query()).expect(200);\n      expect(res.body.parsed).toHaveProperty('page', page);\n      expect(res.body.parsed).toHaveProperty('limit', limit);\n      expect(res.body.parsed).toHaveProperty('fields', fields);\n      expect(res.body.parsed).toHaveProperty('sort');\n      for (let i = 0; i < sorts.length; i++) {\n        expect(res.body.parsed.sort[i]).toHaveProperty('field', sorts[i][0]);\n        expect(res.body.parsed.sort[i]).toHaveProperty('order', sorts[i][1]);\n      }\n      expect(res.body.parsed).toHaveProperty('filter');\n      for (let i = 0; i < filters.length; i++) {\n        expect(res.body.parsed.filter[i]).toHaveProperty('field', filters[i][0]);\n        expect(res.body.parsed.filter[i]).toHaveProperty('operator', filters[i][1]);\n        expect(res.body.parsed.filter[i]).toHaveProperty('value', filters[i][2] || '');\n      }\n    });\n\n    it('should others working', async () => {\n      const res = await $.get('/test/other').query({ page: 2, per_page: 11 }).expect(200);\n      expect(res.body.page).toBe(2);\n    });\n\n    it('should parse param', async () => {\n      const res = await $.get('/test/other2/123').expect(200);\n      expect(res.body.p).toBe(123);\n    });\n\n    it('should parse custom param in crud', async () => {\n      const res = await $.get('/test2/other2/123').expect(200);\n      expect(res.body.p).toBe(123);\n    });\n\n    it('should parse crud param and custom param', async () => {\n      const res = await $.get('/test2/other2/1/twoParams/123').expect(200);\n      expect(res.body.filter).toHaveLength(2);\n      expect(res.body.filter[0].field).toBe('id');\n      expect(res.body.filter[0].value).toBe(1);\n    });\n\n    it('should parse multiple primary key', async () => {\n      const res = await $.get('/test5/123/456').expect(200);\n    });\n\n    it('should work like before', async () => {\n      const res = await $.get('/test2/normal/0').expect(200);\n      expect(res.body.filter).toHaveLength(1);\n      expect(res.body.filter[0].field).toBe('id');\n      expect(res.body.filter[0].value).toBe(0);\n    });\n\n    it('should handle authorized request, 1', async () => {\n      const res = await $.post('/test3').send({}).expect(201);\n      const authPersist = { bar: false };\n      const { parsed } = res.body;\n      expect(parsed.authPersist).toMatchObject(authPersist);\n    });\n\n    it('should handle authorized request, 2', async () => {\n      const res = await $.get('/test3').expect(200);\n      const search = { $and: [{ user: 'test', buz: 1 }, {}] };\n      expect(res.body.parsed.search).toMatchObject(search);\n    });\n\n    it('should handle authorized request, 3', async () => {\n      const query = qb.search({ name: 'test' }).query();\n      const res = await $.get('/test4').query(query).expect(200);\n      const search = { $or: [{ id: 1 }, { $and: [{}, { name: 'test' }] }] };\n      expect(res.body.parsed.search).toMatchObject(search);\n    });\n    it('should handle authorized request, 4', async () => {\n      const query = qb.search({ name: 'test' }).query();\n      const res = await $.get('/test3').query(query).expect(200);\n      const search = { $and: [{ user: 'test', buz: 1 }, { name: 'persist' }] };\n      expect(res.body.parsed.search).toMatchObject(search);\n    });\n\n    it('should handle classTransformOptions, 1', async () => {\n      const res = await $.get('/test6').expect(200);\n      const groups = ['TEST_2'];\n      expect(res.body.parsed.classTransformOptions.groups).toMatchObject(groups);\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud-service.abstract.spec.ts",
    "content": "import { BadRequestException, NotFoundException } from '@nestjs/common';\n\nimport { TestService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#CrudService', () => {\n    let service: TestService<any>;\n\n    beforeAll(() => {\n      service = new TestService();\n    });\n\n    describe('#throwBadRequestException', () => {\n      it('should throw BadRequestException', () => {\n        expect(service.throwBadRequestException.bind(service, '')).toThrowError(\n          BadRequestException,\n        );\n      });\n    });\n\n    describe('#throwNotFoundException', () => {\n      it('should throw NotFoundException', () => {\n        expect(service.throwNotFoundException.bind(service, '')).toThrowError(\n          NotFoundException,\n        );\n      });\n    });\n\n    describe('#createPageInfo', () => {\n      it('should return an object', () => {\n        const expected = {\n          count: 0,\n          data: [],\n          page: 2,\n          pageCount: 10,\n          total: 100,\n        };\n        expect(service.createPageInfo([], 100, 10, 10)).toMatchObject(expected);\n      });\n\n      it('should return an object when limit and offset undefined', () => {\n        const expected = {\n          count: 0,\n          data: [],\n          page: 1,\n          pageCount: 1,\n          total: 100,\n        };\n        expect(service.createPageInfo([], 100, undefined, undefined)).toMatchObject(\n          expected,\n        );\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud.decorator.base.spec.ts",
    "content": "import * as request from 'supertest';\nimport { Test } from '@nestjs/testing';\nimport { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\nimport { RequestQueryBuilder } from '@nestjsx/crud-request';\n\nimport { Crud } from '../src/decorators/crud.decorator';\nimport { CreateManyDto } from '../src/interfaces';\nimport { HttpExceptionFilter } from './__fixture__/exception.filter';\nimport { TestModel } from './__fixture__/models';\nimport { TestService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#base methods', () => {\n    let app: INestApplication;\n    let server: any;\n    let qb: RequestQueryBuilder;\n\n    @Crud({\n      model: { type: TestModel },\n    })\n    @Controller('test')\n    class TestController {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [TestController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    beforeEach(() => {\n      qb = RequestQueryBuilder.create();\n    });\n\n    afterAll(async () => {\n      app.close();\n    });\n\n    describe('#getManyBase', () => {\n      it('should return status 200', (done) => {\n        request(server)\n          .get('/test')\n          .end((_, res) => {\n            expect(res.status).toEqual(200);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        const query = qb.setFilter({ field: 'foo', operator: 'gt' }).query();\n        request(server)\n          .get('/test')\n          .query(query)\n          .end((_, res) => {\n            const expected = { statusCode: 400, message: 'Invalid filter value' };\n            expect(res.status).toEqual(400);\n            expect(res.body).toMatchObject(expected);\n            done();\n          });\n      });\n    });\n\n    describe('#getOneBase', () => {\n      it('should return status 200', (done) => {\n        request(server)\n          .get('/test/1')\n          .end((_, res) => {\n            expect(res.status).toEqual(200);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        request(server)\n          .get('/test/invalid')\n          .end((_, res) => {\n            const expected = {\n              statusCode: 400,\n              message: 'Invalid param id. Number expected',\n            };\n            expect(res.status).toEqual(400);\n            expect(res.body).toMatchObject(expected);\n            done();\n          });\n      });\n    });\n\n    describe('#createOneBase', () => {\n      it('should return status 201', (done) => {\n        const send: TestModel = {\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n          age: 15,\n        };\n        request(server)\n          .post('/test')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(201);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        const send: TestModel = {\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n        };\n        request(server)\n          .post('/test')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(400);\n            done();\n          });\n      });\n    });\n\n    describe('#createMadyBase', () => {\n      it('should return status 201', (done) => {\n        const send: CreateManyDto<TestModel> = {\n          bulk: [\n            {\n              firstName: 'firstName',\n              lastName: 'lastName',\n              email: 'test@test.com',\n              age: 15,\n            },\n            {\n              firstName: 'firstName',\n              lastName: 'lastName',\n              email: 'test@test.com',\n              age: 15,\n            },\n          ],\n        };\n        request(server)\n          .post('/test/bulk')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(201);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        const send: CreateManyDto<TestModel> = {\n          bulk: [],\n        };\n        request(server)\n          .post('/test/bulk')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(400);\n            done();\n          });\n      });\n    });\n\n    describe('#replaceOneBase', () => {\n      it('should return status 200', (done) => {\n        const send: TestModel = {\n          id: 1,\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n          age: 15,\n        };\n        request(server)\n          .put('/test/1')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(200);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        const send: TestModel = {\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n        };\n        request(server)\n          .put('/test/1')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(400);\n            done();\n          });\n      });\n    });\n\n    describe('#updateOneBase', () => {\n      it('should return status 200', (done) => {\n        const send: TestModel = {\n          id: 1,\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n          age: 15,\n        };\n        request(server)\n          .patch('/test/1')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(200);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        const send: TestModel = {\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n        };\n        request(server)\n          .patch('/test/1')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(400);\n            done();\n          });\n      });\n    });\n\n    describe('#deleteOneBase', () => {\n      it('should return status 200', (done) => {\n        request(server)\n          .delete('/test/1')\n          .end((_, res) => {\n            expect(res.status).toEqual(200);\n            done();\n          });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud.decorator.exclude.spec.ts",
    "content": "import * as request from 'supertest';\nimport { Test } from '@nestjs/testing';\nimport { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\n\nimport { Crud } from '../src/decorators';\nimport { HttpExceptionFilter } from './__fixture__/exception.filter';\nimport { TestModel } from './__fixture__/models';\nimport { TestService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#exclude routes', () => {\n    let app: INestApplication;\n    let server: any;\n\n    @Crud({\n      model: { type: TestModel },\n      routes: {\n        exclude: ['getManyBase'],\n      },\n    })\n    @Controller('test')\n    class TestController {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [TestController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      app.close();\n    });\n\n    describe('#getManyBase excluded', () => {\n      it('should return status 404', (done) => {\n        request(server)\n          .get('/test')\n          .end((_, res) => {\n            expect(res.status).toEqual(404);\n            done();\n          });\n      });\n    });\n  });\n\n  describe('#only routes', () => {\n    let app: INestApplication;\n    let server: any;\n\n    @Crud({\n      model: { type: TestModel },\n      routes: {\n        only: ['getManyBase'],\n      },\n    })\n    @Controller('test')\n    class TestController {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [TestController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      app.close();\n    });\n\n    describe('#getManyBase only', () => {\n      it('should return status 200', (done) => {\n        request(server)\n          .get('/test')\n          .end((_, res) => {\n            expect(res.status).toEqual(200);\n            done();\n          });\n      });\n    });\n\n    describe('#getOneBase excluded', () => {\n      it('should return status 404', (done) => {\n        request(server)\n          .get('/test/1')\n          .end((_, res) => {\n            expect(res.status).toEqual(404);\n            done();\n          });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud.decorator.options.spec.ts",
    "content": "import * as request from 'supertest';\nimport { Test } from '@nestjs/testing';\nimport { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\nimport { CrudRoutesFactory } from '../src/crud/crud-routes.factory';\nimport { Swagger } from '../src/crud/swagger.helper';\nimport { Crud } from '../src/decorators';\nimport { CrudOptions } from '../src/interfaces';\nimport { BaseRouteName } from '../src/types';\nimport { HttpExceptionFilter } from './__fixture__/exception.filter';\nimport { TestModel } from './__fixture__/models';\nimport { TestService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#options', () => {\n    let app: INestApplication;\n    let server: any;\n\n    class CustomSwaggerRoutesFactory extends CrudRoutesFactory {\n      protected setSwaggerOperation(name: BaseRouteName) {\n        const summary = Swagger.operationsMap(this.modelName)[name];\n        const operationId = '_' + name + this.modelName;\n        Swagger.setOperation({ summary, operationId }, this.targetProto[name]);\n      }\n    }\n\n    const options: CrudOptions = {\n      model: { type: TestModel },\n      params: {\n        id: {\n          field: 'id',\n          type: 'uuid',\n          primary: true,\n        },\n      },\n      query: {\n        limit: 10,\n      },\n      routes: {\n        getManyBase: {\n          interceptors: [],\n          decorators: [],\n        },\n        getOneBase: {\n          interceptors: [],\n          decorators: [],\n        },\n        createOneBase: {\n          interceptors: [],\n          decorators: [],\n        },\n        createManyBase: {\n          interceptors: [],\n          decorators: [],\n        },\n        updateOneBase: {\n          interceptors: [],\n          decorators: [],\n          allowParamsOverride: true,\n        },\n        replaceOneBase: {\n          interceptors: [],\n          decorators: [],\n          allowParamsOverride: true,\n        },\n        deleteOneBase: {\n          interceptors: [],\n          decorators: [],\n          returnDeleted: true,\n        },\n      },\n      routesFactory: CustomSwaggerRoutesFactory,\n    };\n\n    @Crud(options)\n    @Controller('test')\n    class TestController {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [TestController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      app.close();\n    });\n\n    it('should return options in ParsedRequest', (done) => {\n      request(server)\n        .get('/test')\n        .expect(200)\n        .end((_, res) => {\n          const opt = res.body.req.options;\n          expect(opt.query).toMatchObject(options.query);\n          expect(opt.routes).toMatchObject(options.routes);\n          expect(opt.params).toMatchObject(options.params);\n          done();\n        });\n    });\n\n    it('should use crudRoutesFactory override', () => {\n      const testController = app.get(TestController);\n      const { operationId } = Swagger.getOperation((testController as any).replaceOneBase);\n      expect(operationId).toEqual('_replaceOneBaseTestModel');\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud.decorator.override.spec.ts",
    "content": "import * as request from 'supertest';\nimport { Test } from '@nestjs/testing';\nimport { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\nimport { RequestQueryBuilder } from '@nestjsx/crud-request';\n\nimport { Crud, Override, ParsedRequest, ParsedBody } from '../src/decorators';\nimport { CrudController, CrudRequest, CreateManyDto } from '../src/interfaces';\nimport { R, Swagger } from '../src/crud';\nimport { CrudActions } from '../src/enums';\nimport { HttpExceptionFilter } from './__fixture__/exception.filter';\nimport { TestModel } from './__fixture__/models';\nimport { TestService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#override methods', () => {\n    let app: INestApplication;\n    let server: any;\n    let qb: RequestQueryBuilder;\n\n    enum Field {\n      ONE = 'one',\n    }\n\n    @Crud({\n      model: { type: TestModel },\n      params: {\n        enumField: {\n          field: 'enum_field',\n          type: 'string',\n          enum: Field,\n        },\n        disabledField: {\n          field: 'disabled_field',\n          type: 'number',\n          disabled: true,\n        },\n      },\n    })\n    @Controller('test')\n    class TestController implements CrudController<TestModel> {\n      constructor(public service: TestService<TestModel>) {}\n\n      get base(): CrudController<TestModel> {\n        return this;\n      }\n\n      @Override()\n      getMany(@ParsedRequest() req: CrudRequest) {\n        return { foo: 'bar' };\n      }\n\n      @Override('createManyBase')\n      createBulk(@ParsedBody() dto: CreateManyDto<TestModel>, @ParsedRequest() req: CrudRequest) {\n        return this.base.createManyBase(req, dto);\n      }\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [TestController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    beforeEach(() => {\n      qb = RequestQueryBuilder.create();\n    });\n\n    afterAll(async () => {\n      app.close();\n    });\n\n    describe('#override getMany', () => {\n      it('should return status 200', (done) => {\n        request(server)\n          .get('/test')\n          .expect(200)\n          .end((_, res) => {\n            const expected = { foo: 'bar' };\n            expect(res.body).toMatchObject(expected);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        const query = qb.setFilter({ field: 'foo', operator: 'gt' }).query();\n        request(server)\n          .get('/test')\n          .query(query)\n          .end((_, res) => {\n            const expected = { statusCode: 400, message: 'Invalid filter value' };\n            expect(res.status).toEqual(400);\n            expect(res.body).toMatchObject(expected);\n            done();\n          });\n      });\n      it('should have action metadata', () => {\n        const action = R.getAction(TestController.prototype.getMany);\n        expect(action).toBe(CrudActions.ReadAll);\n      });\n      it('should return swagger operation', () => {\n        const operation = Swagger.getOperation(TestController.prototype.getMany);\n        const expected = { summary: 'Retrieve multiple TestModels' };\n        expect(operation).toMatchObject(expected);\n      });\n      it('should return swagger params', () => {\n        const params = Swagger.getParams(TestController.prototype.getMany);\n        expect(Array.isArray(params)).toBe(true);\n        expect(params.length > 0).toBe(true);\n\n        const enumParam = params.find((param) => param.name === 'enumField');\n        expect(enumParam).toBeDefined();\n        expect(enumParam.enum).toEqual(['one']);\n      });\n      it('should not return disabled fields in swagger', () => {\n        const params = Swagger.getParams(TestController.prototype.getMany);\n        expect(Array.isArray(params)).toBe(true);\n        expect(params.length > 0).toBe(true);\n\n        const disabledParam = params.find((param) => param.name === 'disabledField');\n        expect(disabledParam).toBeUndefined();\n      });\n      it('should return swagger response ok', () => {\n        const response = Swagger.getResponseOk(TestController.prototype.getMany);\n        const expected = {\n          '200': {\n            schema: {\n              oneOf: [\n                { $ref: '#/components/schemas/GetManyTestModelResponseDto' },\n                { items: { $ref: '#/components/schemas/TestModel' }, type: 'array' },\n              ],\n            },\n          },\n        };\n        expect(response).toMatchObject(expected);\n      });\n    });\n\n    describe('#override createMany', () => {\n      it('should still validate dto', (done) => {\n        const send: CreateManyDto<TestModel> = {\n          bulk: [],\n        };\n        request(server)\n          .post('/test/bulk')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(400);\n            done();\n          });\n      });\n      it('should return status 201', (done) => {\n        const send: CreateManyDto<TestModel> = {\n          bulk: [\n            {\n              firstName: 'firstName',\n              lastName: 'lastName',\n              email: 'test@test.com',\n              age: 15,\n            },\n            {\n              firstName: 'firstName',\n              lastName: 'lastName',\n              email: 'test@test.com',\n              age: 15,\n            },\n          ],\n        };\n        request(server)\n          .post('/test/bulk')\n          .send(send)\n          .expect(201)\n          .end((_, res) => {\n            expect(res.body).toHaveProperty('req');\n            expect(res.body).toHaveProperty('dto');\n            expect(res.body.dto).toMatchObject(send);\n            done();\n          });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud.decorator.soft.spec.ts",
    "content": "import * as request from 'supertest';\nimport { Test } from '@nestjs/testing';\nimport { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\n\nimport { Crud } from '../src/decorators';\nimport { HttpExceptionFilter } from './__fixture__/exception.filter';\nimport { TestModel } from './__fixture__/models';\nimport { TestService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#soft delete disabled', () => {\n    let app: INestApplication;\n    let server: any;\n\n    @Crud({\n      model: { type: TestModel },\n    })\n    @Controller('test')\n    class TestController {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [TestController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      app.close();\n    });\n\n    describe('#recoverOneBase', () => {\n      it('should return status 404 if controller does not have soft delete', (done) => {\n        request(server)\n          .patch('/test/1/recover')\n          .end((_, res) => {\n            expect(res.status).toEqual(404);\n            done();\n          });\n      });\n    });\n  });\n\n  describe('#soft delete enabled', () => {\n    let app: INestApplication;\n    let server: any;\n\n    @Crud({\n      model: { type: TestModel },\n      query: {\n        softDelete: true,\n      },\n    })\n    @Controller('test')\n    class TestController {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [TestController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      app.close();\n    });\n\n    describe('#recoverOneBase', () => {\n      it('should return status 200 if controller has soft delete', (done) => {\n        request(server)\n          .patch('/test/1/recover')\n          .end((_, res) => {\n            expect(res.status).toEqual(200);\n            done();\n          });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud.dto.options.spec.ts",
    "content": "import * as request from 'supertest';\nimport { Test } from '@nestjs/testing';\nimport { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\n\nimport { Crud } from '../src/decorators/crud.decorator';\nimport { HttpExceptionFilter } from './__fixture__/exception.filter';\nimport { TestModel } from './__fixture__/models';\nimport { TestCreateDto, TestUpdateDto } from './__fixture__/dto';\nimport { TestService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#dto options', () => {\n    let app: INestApplication;\n    let server: any;\n\n    @Crud({\n      model: {\n        type: TestModel,\n      },\n      dto: {\n        create: TestCreateDto,\n        update: TestUpdateDto,\n      },\n    })\n    @Controller('test')\n    class TestController {\n      constructor(public service: TestService<TestModel>) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [TestController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, TestService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      await app.close();\n    });\n\n    describe('#createOneBase', () => {\n      it('should return status 201', (done) => {\n        const send: TestCreateDto = {\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n          age: 15,\n        };\n        request(server)\n          .post('/test')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(201);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        const send: TestModel = {\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n        };\n        request(server)\n          .post('/test')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(400);\n            done();\n          });\n      });\n    });\n\n    describe('#updateOneBase', () => {\n      it('should return status 200', (done) => {\n        const send: TestModel = {\n          id: 1,\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'test@test.com',\n          age: 15,\n        };\n        request(server)\n          .patch('/test/1')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(200);\n            done();\n          });\n      });\n      it('should return status 400', (done) => {\n        const send: TestModel = {\n          firstName: 'firstName',\n          lastName: 'lastName',\n          email: 'foo',\n        };\n        request(server)\n          .patch('/test/1')\n          .send(send)\n          .end((_, res) => {\n            expect(res.status).toEqual(400);\n            done();\n          });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/crud.serialize.options.spec.ts",
    "content": "import * as request from 'supertest';\nimport { Test } from '@nestjs/testing';\nimport { Controller, INestApplication, Inject } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\n\nimport { Crud, Override, ParsedRequest } from '../src/decorators';\nimport { CrudController, CrudRequest } from '../src/interfaces';\nimport { HttpExceptionFilter } from './__fixture__/exception.filter';\nimport { TestSerializeModel, TestSerialize2Model } from './__fixture__/models';\nimport {\n  GetModelResponseDto,\n  GetManyModelResponseDto,\n  DeleteModelResponseDto,\n  RecoverModelResponseDto,\n} from './__fixture__/response';\nimport { TestSerializeService } from './__fixture__/services';\n\ndescribe('#crud', () => {\n  describe('#serialize options', () => {\n    let app: INestApplication;\n    let server: any;\n\n    const SERVICE_TOKEN = 'TestSerializeServiceToken';\n    const SERVICE2_TOKEN = 'TestSerializeServiceToken2';\n\n    @Crud({\n      model: {\n        type: TestSerializeModel,\n      },\n      serialize: {\n        get: GetModelResponseDto,\n        delete: DeleteModelResponseDto,\n      },\n      routes: {\n        deleteOneBase: {\n          returnDeleted: true,\n        },\n      },\n    })\n    @Controller('test')\n    class TestController {\n      constructor(@Inject(SERVICE_TOKEN) public service: TestSerializeService) {}\n    }\n\n    @Crud({\n      model: {\n        type: TestSerialize2Model,\n      },\n      serialize: {\n        get: GetModelResponseDto,\n      },\n      routes: {\n        deleteOneBase: {\n          returnDeleted: true,\n        },\n      },\n      query: {\n        alwaysPaginate: true,\n      },\n    })\n    @Controller('test2')\n    class Test2Controller {\n      constructor(@Inject(SERVICE2_TOKEN) public service: TestSerializeService) {}\n    }\n\n    @Crud({\n      model: {\n        type: TestSerialize2Model,\n      },\n      serialize: {\n        get: GetModelResponseDto,\n        getMany: GetManyModelResponseDto,\n      },\n    })\n    @Controller('test3')\n    class Test3Controller implements CrudController<TestSerialize2Model> {\n      constructor(@Inject(SERVICE2_TOKEN) public service: TestSerializeService) {}\n\n      get base(): CrudController<TestSerialize2Model> {\n        return this;\n      }\n\n      @Override()\n      async getMany(@ParsedRequest() req: CrudRequest) {\n        const items = (await this.base.getManyBase(req)) as TestSerialize2Model[];\n        const response = new GetManyModelResponseDto();\n        response.items = items;\n        return response;\n      }\n    }\n\n    @Crud({\n      model: {\n        type: TestSerialize2Model,\n      },\n    })\n    @Controller('test4')\n    class Test4Controller {\n      constructor(@Inject(SERVICE2_TOKEN) public service: TestSerializeService) {}\n    }\n\n    @Crud({\n      model: {\n        type: { name: 'SomeModel' },\n      },\n      serialize: {\n        get: false,\n        getMany: false,\n        create: false,\n        createMany: false,\n        update: false,\n        replace: false,\n        recover: false,\n      },\n    })\n    @Controller('test5')\n    class Test5Controller {\n      constructor(@Inject(SERVICE2_TOKEN) public service: TestSerializeService) {}\n    }\n\n    @Crud({\n      model: {\n        type: TestSerializeModel,\n      },\n      serialize: {\n        get: GetModelResponseDto,\n        recover: RecoverModelResponseDto,\n      },\n      query: {\n        softDelete: true,\n      },\n      routes: {\n        recoverOneBase: {\n          returnRecovered: true,\n        },\n      },\n    })\n    @Controller('test6')\n    class Test6Controller {\n      constructor(@Inject(SERVICE_TOKEN) public service: TestSerializeService) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        controllers: [\n          TestController,\n          Test2Controller,\n          Test3Controller,\n          Test4Controller,\n          Test5Controller,\n          Test6Controller,\n        ],\n        providers: [\n          { provide: APP_FILTER, useClass: HttpExceptionFilter },\n          {\n            provide: SERVICE_TOKEN,\n            useFactory: () => new TestSerializeService(TestSerializeModel),\n          },\n          {\n            provide: SERVICE2_TOKEN,\n            useFactory: () => new TestSerializeService(TestSerialize2Model),\n          },\n        ],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      await app.close();\n    });\n\n    describe('#getManyBase', () => {\n      it('should return an array', (done) => {\n        request(server)\n          .get('/test')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.length).toBe(5);\n            expect(res.body[0].email).toBeUndefined();\n            expect(res.body[1].email).toBeUndefined();\n            done();\n          });\n      });\n      it('should return an object', (done) => {\n        request(server)\n          .get('/test2')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.data.length).toBe(5);\n            expect(res.body.data[0].email).toBeUndefined();\n            expect(res.body.data[1].email).toBeUndefined();\n            done();\n          });\n      });\n      it('should return custom response', (done) => {\n        request(server)\n          .get('/test3')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.items.length).toBe(5);\n            expect(res.body.items[0].email).toBeUndefined();\n            expect(res.body.items[1].email).toBeUndefined();\n            done();\n          });\n      });\n    });\n\n    describe('#getOneBase', () => {\n      it('should return model', (done) => {\n        request(server)\n          .get('/test4/1')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.email).toBeDefined();\n            expect(res.body.isActive).toBeUndefined();\n            done();\n          });\n      });\n      it('should return serialized model', (done) => {\n        request(server)\n          .get('/test/1')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.isActive).toBeDefined();\n            expect(res.body.email).toBeUndefined();\n            done();\n          });\n      });\n      it('should return model without serializing', (done) => {\n        request(server)\n          .get('/test5/1')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.id).toBeDefined();\n            expect(res.body.name).toBeDefined();\n            expect(res.body.email).toBeDefined();\n            expect(res.body.isActive).toBeDefined();\n            done();\n          });\n      });\n    });\n\n    describe('#deleteManyBase', () => {\n      it('should return serialized model', (done) => {\n        request(server)\n          .delete('/test/1')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.id).toBeDefined();\n            expect(res.body.name).toBeUndefined();\n            expect(res.body.email).toBeUndefined();\n            expect(res.body.isActive).toBeUndefined();\n            done();\n          });\n      });\n      it('should return model', (done) => {\n        request(server)\n          .delete('/test2/1')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.id).toBeDefined();\n            expect(res.body.name).toBeDefined();\n            expect(res.body.email).toBeDefined();\n            expect(res.body.isActive).toBeUndefined();\n            done();\n          });\n      });\n      it('should return en empty response', (done) => {\n        request(server)\n          .delete('/test3/1')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body).toMatchObject({});\n            done();\n          });\n      });\n    });\n\n    describe('#recoverOneBase', () => {\n      it('should return model', (done) => {\n        request(server)\n          .patch('/test6/1/recover')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body.id).toBeDefined();\n            expect(res.body.name).toBeDefined();\n            expect(res.body.email).toBeDefined();\n            expect(res.body.isActive).toBeDefined();\n            done();\n          });\n      });\n      it('should return en empty response', (done) => {\n        request(server)\n          .patch('/test3/1/recover')\n          .expect(200)\n          .end((_, res) => {\n            expect(res.body).toMatchObject({});\n            done();\n          });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/test/feature-action.decorator.spec.ts",
    "content": "import { Feature, Action, getFeature, getAction } from '../src/decorators';\n\ndescribe('#crud', () => {\n  const feature = 'feature';\n  const action = 'action';\n\n  @Feature(feature)\n  class TestClass {\n    @Action(action)\n    root() {}\n  }\n\n  describe('#feature decorator', () => {\n    it('should save metadata', () => {\n      const metadata = getFeature(TestClass);\n      expect(metadata).toBe(feature);\n    });\n  });\n  describe('#action decorator', () => {\n    it('should save metadata', () => {\n      const metadata = getAction(TestClass.prototype.root);\n      expect(metadata).toBe(action);\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"rootDir\": \"src\",\n    \"outDir\": \"lib\"\n  },\n  \"include\": [\"src\"],\n  \"exclude\": [\"lib\"],\n  \"references\": [{ \"path\": \"../util\" }, { \"path\": \"../crud-request\" }]\n}\n"
  },
  {
    "path": "packages/crud-request/README.md",
    "content": "<div align=\"center\">\n  <h1>CRUD (@nestjsx/crud-request)</h1>\n</div>\n<div align=\"center\">\n  <strong>for RESTful APIs built with NestJs</strong>\n</div>\n\n<br />\n\n<div align=\"center\">\n  <a href=\"https://travis-ci.org/nestjsx/crud\">\n    <img src=\"https://github.com/nestjsx/crud/workflows/Tests/badge.svg\" alt=\"Build\" />\n  </a>\n  <a href=\"https://coveralls.io/github/nestjsx/crud?branch=master\">\n    <img src=\"https://coveralls.io/repos/github/nestjsx/crud/badge.svg\" alt=\"Coverage\" />\n  </a>\n  <a href=\"https://github.com/nestjsx/crud/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/nestjsx/crud.svg\" alt=\"License\" />\n  </a>\n  <a href=\"https://www.npmjs.com/package/@nestjsx/crud\">\n    <img src=\"https://img.shields.io/npm/v/@nestjsx/crud.svg\" alt=\"npm version\" />\n  </a>\n  <a href=\"https://www.npmjs.com/org/nestjsx\">\n    <img src=\"https://img.shields.io/npm/dm/@nestjsx/crud.svg\" alt=\"npm downloads\" />\n  </a>\n  <a href=\"https://npm.packagequality.com/#?package=@nestjsx%2Fcrud\">\n    <img src=\"https://npm.packagequality.com/shield/%40nestjsx%2Fcrud.svg\" alt=\"Package Quality\" />\n  </a>\n  <a href=\"https://renovatebot.com/\">\n    <img src=\"https://img.shields.io/badge/renovate-enabled-brightgreen.svg\" alt=\"Renovate\" />\n  </a>\n  <a href=\"http://makeapullrequest.com\">\n    <img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\" alt=\"PRs welcome\" />\n  </a>\n  <a href=\"https://github.com/marmelab/awesome-rest#nodejs\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-rest.svg?sanitize=true\" alt=\"Awesome REST\" />\n  </a>\n  <a href=\"https://github.com/juliandavidmr/awesome-nestjs#components--libraries\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-nest.svg?sanitize=true\" alt=\"Awesome Nest\" />\n  </a>\n  <a href=\"https://github.com/nestjs/nest\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/nest-powered.svg?sanitize=true\" alt=\"Nest Powered\" />\n  </a>\n  <a href=\"#individuals\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/backers/badge.svg\" />\n  </a>\n  <a href=\"#organizations\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/sponsors/badge.svg\" />\n  </a> \n</div>\n\n<div align=\"center\">\n  <sub>Built by\n  <a href=\"https://twitter.com/MichaelYali\">@MichaelYali</a> and\n  <a href=\"https://github.com/nestjsx/crud/graphs/contributors\">\n    Contributors\n  </a>\n</div>\n\n<br />\n\nWe believe that everyone who's working with NestJs and building some RESTful services and especially some CRUD functionality will find `@nestjsx/crud` microframework very useful.\n\n## Features\n\n<img align=\"right\" src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/crud-usage2.png\" alt=\"CRUD usage\" />\n\n- Super easy to install and start using the full-featured controllers and services :point_right:\n\n- DB and service agnostic extendable CRUD controllers\n\n- Reach query parsing with filtering, pagination, sorting, relations, nested relations, cache, etc.\n\n- Framework agnostic package with query builder for a frontend usage\n\n- Query, path params and DTOs validation included\n\n- Overriding controller methods with ease\n\n- Tiny config (including globally)\n\n- Additional helper decorators\n\n- Swagger documentation\n\n## Install\n\n```shell\nnpm i @nestjsx/crud-request\n```\n\n## Packages\n\n- [**@nestjsx/crud**](https://www.npmjs.com/package/@nestjsx/crud) - core package which provides `@Crud()` decorator for endpoints generation, global configuration, validation, helper decorators ([docs](https://github.com/nestjsx/crud/wiki/Controllers#description))\n- [**@nestjsx/crud-request**](https://www.npmjs.com/package/@nestjsx/crud-request) - request builder/parser package which provides `RequestQueryBuilder` class for a frontend usage and `RequestQueryParser` that is being used internally for handling and validating query/path params on a backend side ([docs](https://github.com/nestjsx/crud/wiki/Requests#frontend-usage))\n- [**@nestjsx/crud-typeorm**](https://www.npmjs.com/package/@nestjsx/crud-typeorm) - TypeORM package which provides base `TypeOrmCrudService` with methods for CRUD database operations ([docs](https://github.com/nestjsx/crud/wiki/ServiceTypeorm))\n\n## Documentation\n\n- [General Information](https://github.com/nestjsx/crud/wiki#why)\n- [CRUD Controllers](https://github.com/nestjsx/crud/wiki/Controllers#description)\n- [CRUD ORM Services](https://github.com/nestjsx/crud/wiki/Services#description)\n- [Handling Requests](https://github.com/nestjsx/crud/wiki/Requests#description)\n\n## Support\n\nAny support is welcome. At least you can give us a star.\n\n## Contributors\n\n### Code Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].\n<a href=\"https://github.com/nestjsx/crud/graphs/contributors\"><img src=\"https://opencollective.com/nestjsx/contributors.svg?width=890&button=false\" /></a>\n\n### Financial Contributors\n\nBecome a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/nestjsx#backer)]\n\n#### Individuals\n\n<a href=\"https://opencollective.com/nestjsx#backers\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/backers.svg?width=890&button=false\"></a>\n\n#### Organizations\n\nSupport this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/nestjsx#sponsor)]\n\n<a href=\"https://opencollective.com/nestjsx/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/9/avatar.svg\"></a>\n\n## License\n\n[MIT](LICENSE)\n"
  },
  {
    "path": "packages/crud-request/package.json",
    "content": "{\n  \"name\": \"@nestjsx/crud-request\",\n  \"description\": \"NestJs CRUD for RESTful APIs - request query builder\",\n  \"version\": \"5.0.0-alpha.3\",\n  \"license\": \"MIT\",\n  \"main\": \"lib/index.js\",\n  \"typings\": \"lib/index.d.ts\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"files\": [\n    \"lib\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/nestjsx/crud.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/nestjsx/crud/issues\"\n  },\n  \"keywords\": [\n    \"typescript\",\n    \"typeorm\",\n    \"nest\",\n    \"nestjs\",\n    \"rest\",\n    \"restful\",\n    \"api\",\n    \"crud\",\n    \"crud-generator\",\n    \"http\",\n    \"request\",\n    \"request-query\",\n    \"requestquery\",\n    \"get\",\n    \"query\",\n    \"query-string\",\n    \"querystring\",\n    \"query-builder\",\n    \"querybuilder\"\n  ],\n  \"author\": {\n    \"name\": \"Michael Yali\",\n    \"email\": \"mihon4ik@gmail.com\"\n  },\n  \"scripts\": {\n    \"build\": \"npx tsc -b\"\n  },\n  \"dependencies\": {\n    \"@nestjsx/util\": \"^5.0.0-alpha.3\",\n    \"qs\": \"^6.10.3\"\n  }\n}\n"
  },
  {
    "path": "packages/crud-request/src/exceptions/index.ts",
    "content": "export * from './request-query.exception';\n"
  },
  {
    "path": "packages/crud-request/src/exceptions/request-query.exception.ts",
    "content": "export class RequestQueryException extends Error {\n  constructor(msg: string) {\n    super(msg);\n  }\n}\n"
  },
  {
    "path": "packages/crud-request/src/index.ts",
    "content": "export * from './exceptions';\nexport * from './request-query.builder';\nexport * from './request-query.parser';\nexport * from './interfaces';\nexport * from './types';\n"
  },
  {
    "path": "packages/crud-request/src/interfaces/create-query-params.interface.ts",
    "content": "import {\n  QueryFields,\n  QueryFilter,\n  QueryFilterArr,\n  QueryJoin,\n  QueryJoinArr,\n  QuerySort,\n  QuerySortArr,\n  SCondition,\n} from '../types';\n\nexport interface CreateQueryParams {\n  fields?: QueryFields;\n  search?: SCondition;\n  filter?: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>;\n  or?: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>;\n  join?: QueryJoin | QueryJoinArr | Array<QueryJoin | QueryJoinArr>;\n  sort?: QuerySort | QuerySortArr | Array<QuerySort | QuerySortArr>;\n  limit?: number;\n  offset?: number;\n  page?: number;\n  resetCache?: boolean;\n  includeDeleted?: number;\n}\n"
  },
  {
    "path": "packages/crud-request/src/interfaces/index.ts",
    "content": "export * from './request-query-builder-options.interface';\nexport * from './params-options.interface';\nexport * from './parsed-request.interface';\nexport * from './create-query-params.interface';\n"
  },
  {
    "path": "packages/crud-request/src/interfaces/params-options.interface.ts",
    "content": "import { ParamOptionType } from '../types';\n\nexport interface ParamsOptions {\n  [key: string]: ParamOption;\n}\n\nexport interface ParamOption {\n  field?: string;\n  type?: ParamOptionType;\n  primary?: boolean;\n  disabled?: boolean;\n}\n"
  },
  {
    "path": "packages/crud-request/src/interfaces/parsed-request.interface.ts",
    "content": "import { ObjectLiteral } from '@nestjsx/util';\nimport { ClassTransformOptions } from 'class-transformer';\nimport { QueryFields, QueryFilter, QueryJoin, QuerySort, SCondition } from '../types';\n\nexport interface ParsedRequestParams {\n  fields: QueryFields;\n  paramsFilter: QueryFilter[];\n  authPersist: ObjectLiteral;\n  classTransformOptions: ClassTransformOptions;\n  search: SCondition;\n  filter: QueryFilter[];\n  or: QueryFilter[];\n  join: QueryJoin[];\n  sort: QuerySort[];\n  limit: number;\n  offset: number;\n  page: number;\n  cache: number;\n  includeDeleted: number;\n}\n"
  },
  {
    "path": "packages/crud-request/src/interfaces/request-query-builder-options.interface.ts",
    "content": "export interface RequestQueryBuilderOptions {\n  delim?: string;\n  delimStr?: string;\n  paramNamesMap?: {\n    fields?: string | string[];\n    search?: string | string[];\n    filter?: string | string[];\n    or?: string | string[];\n    join?: string | string[];\n    sort?: string | string[];\n    limit?: string | string[];\n    offset?: string | string[];\n    page?: string | string[];\n    cache?: string | string[];\n    includeDeleted?: string | string[];\n  };\n}\n"
  },
  {
    "path": "packages/crud-request/src/request-query.builder.ts",
    "content": "import { hasValue, isObject, isString, isArrayFull, isNil, isUndefined } from '@nestjsx/util';\nimport { stringify } from 'qs';\n\nimport { RequestQueryBuilderOptions, CreateQueryParams } from './interfaces';\nimport {\n  validateCondition,\n  validateFields,\n  validateJoin,\n  validateNumeric,\n  validateSort,\n} from './request-query.validator';\nimport {\n  QueryFields,\n  QueryFilter,\n  QueryFilterArr,\n  QueryJoin,\n  QueryJoinArr,\n  QuerySort,\n  QuerySortArr,\n  SCondition,\n} from './types';\n\n// tslint:disable:variable-name ban-types\nexport class RequestQueryBuilder {\n  private static _options: RequestQueryBuilderOptions = {\n    delim: '||',\n    delimStr: ',',\n    paramNamesMap: {\n      fields: ['fields', 'select'],\n      search: 's',\n      filter: 'filter',\n      or: 'or',\n      join: 'join',\n      sort: 'sort',\n      limit: ['limit', 'per_page'],\n      offset: 'offset',\n      page: 'page',\n      cache: 'cache',\n      includeDeleted: 'include_deleted',\n    },\n  };\n\n  public queryObject: { [key: string]: any } = {};\n\n  public queryString: string;\n\n  private paramNames: {\n    [key in keyof RequestQueryBuilderOptions['paramNamesMap']]: string;\n  } = {};\n\n  constructor() {\n    this.setParamNames();\n  }\n\n  static setOptions(options: RequestQueryBuilderOptions) {\n    RequestQueryBuilder._options = {\n      ...RequestQueryBuilder._options,\n      ...options,\n      paramNamesMap: {\n        ...RequestQueryBuilder._options.paramNamesMap,\n        ...(options.paramNamesMap ? options.paramNamesMap : {}),\n      },\n    };\n  }\n\n  static getOptions(): RequestQueryBuilderOptions {\n    return RequestQueryBuilder._options;\n  }\n\n  static create(params?: CreateQueryParams): RequestQueryBuilder {\n    const qb = new RequestQueryBuilder();\n    return isObject(params) ? qb.createFromParams(params) : qb;\n  }\n\n  get options(): RequestQueryBuilderOptions {\n    return RequestQueryBuilder._options;\n  }\n\n  setParamNames() {\n    Object.keys(RequestQueryBuilder._options.paramNamesMap).forEach((key) => {\n      const name = RequestQueryBuilder._options.paramNamesMap[key];\n      this.paramNames[key] = isString(name) ? (name as string) : (name[0] as string);\n    });\n  }\n\n  query(encode = true): string {\n    if (this.queryObject[this.paramNames.search]) {\n      this.queryObject[this.paramNames.filter] = undefined;\n      this.queryObject[this.paramNames.or] = undefined;\n    }\n    this.queryString = stringify(this.queryObject, { encode });\n    return this.queryString;\n  }\n\n  select(fields: QueryFields): this {\n    if (isArrayFull(fields)) {\n      validateFields(fields);\n      this.queryObject[this.paramNames.fields] = fields.join(this.options.delimStr);\n    }\n    return this;\n  }\n\n  search(s: SCondition) {\n    if (!isNil(s) && isObject(s)) {\n      this.queryObject[this.paramNames.search] = JSON.stringify(s);\n    }\n    return this;\n  }\n\n  setFilter(f: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>): this {\n    this.setCondition(f, 'filter');\n    return this;\n  }\n\n  setOr(f: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>): this {\n    this.setCondition(f, 'or');\n    return this;\n  }\n\n  setJoin(j: QueryJoin | QueryJoinArr | Array<QueryJoin | QueryJoinArr>): this {\n    if (!isNil(j)) {\n      const param = this.checkQueryObjectParam('join', []);\n      this.queryObject[param] = [\n        ...this.queryObject[param],\n        ...(Array.isArray(j) && !isString(j[0])\n          ? (j as Array<QueryJoin | QueryJoinArr>).map((o) => this.addJoin(o))\n          : [this.addJoin(j as QueryJoin | QueryJoinArr)]),\n      ];\n    }\n    return this;\n  }\n\n  sortBy(s: QuerySort | QuerySortArr | Array<QuerySort | QuerySortArr>): this {\n    if (!isNil(s)) {\n      const param = this.checkQueryObjectParam('sort', []);\n      this.queryObject[param] = [\n        ...this.queryObject[param],\n        ...(Array.isArray(s) && !isString(s[0])\n          ? (s as Array<QuerySort | QuerySortArr>).map((o) => this.addSortBy(o))\n          : [this.addSortBy(s as QuerySort | QuerySortArr)]),\n      ];\n    }\n    return this;\n  }\n\n  setLimit(n: number): this {\n    this.setNumeric(n, 'limit');\n    return this;\n  }\n\n  setOffset(n: number): this {\n    this.setNumeric(n, 'offset');\n    return this;\n  }\n\n  setPage(n: number): this {\n    this.setNumeric(n, 'page');\n    return this;\n  }\n\n  resetCache(): this {\n    this.setNumeric(0, 'cache');\n    return this;\n  }\n\n  setIncludeDeleted(n: number): this {\n    this.setNumeric(n, 'includeDeleted');\n    return this;\n  }\n\n  cond(f: QueryFilter | QueryFilterArr, cond: 'filter' | 'or' | 'search' = 'search'): string {\n    const filter = Array.isArray(f) ? { field: f[0], operator: f[1], value: f[2] } : f;\n    validateCondition(filter, cond);\n    const d = this.options.delim;\n\n    return filter.field + d + filter.operator + (hasValue(filter.value) ? d + filter.value : '');\n  }\n\n  private addJoin(j: QueryJoin | QueryJoinArr): string {\n    const join = Array.isArray(j) ? { field: j[0], select: j[1] } : j;\n    validateJoin(join);\n    const d = this.options.delim;\n    const ds = this.options.delimStr;\n\n    return join.field + (isArrayFull(join.select) ? d + join.select.join(ds) : '');\n  }\n\n  private addSortBy(s: QuerySort | QuerySortArr): string {\n    const sort = Array.isArray(s) ? { field: s[0], order: s[1] } : s;\n    validateSort(sort);\n    const ds = this.options.delimStr;\n\n    return sort.field + ds + sort.order;\n  }\n\n  private createFromParams(params: CreateQueryParams): this {\n    this.select(params.fields);\n    this.search(params.search);\n    this.setFilter(params.filter);\n    this.setOr(params.or);\n    this.setJoin(params.join);\n    this.setLimit(params.limit);\n    this.setOffset(params.offset);\n    this.setPage(params.page);\n    this.sortBy(params.sort);\n    if (params.resetCache) {\n      this.resetCache();\n    }\n    this.setIncludeDeleted(params.includeDeleted);\n    return this;\n  }\n\n  private checkQueryObjectParam(cond: keyof RequestQueryBuilderOptions['paramNamesMap'], defaults: any): string {\n    const param = this.paramNames[cond];\n    if (isNil(this.queryObject[param]) && !isUndefined(defaults)) {\n      this.queryObject[param] = defaults;\n    }\n    return param;\n  }\n\n  private setCondition(\n    f: QueryFilter | QueryFilterArr | Array<QueryFilter | QueryFilterArr>,\n    cond: 'filter' | 'or',\n  ): void {\n    if (!isNil(f)) {\n      const param = this.checkQueryObjectParam(cond, []);\n      this.queryObject[param] = [\n        ...this.queryObject[param],\n        ...(Array.isArray(f) && !isString(f[0])\n          ? (f as Array<QueryFilter | QueryFilterArr>).map((o) => this.cond(o, cond))\n          : [this.cond(f as QueryFilter | QueryFilterArr, cond)]),\n      ];\n    }\n  }\n\n  private setNumeric(n: number, cond: 'limit' | 'offset' | 'page' | 'cache' | 'includeDeleted'): void {\n    if (!isNil(n)) {\n      validateNumeric(n, cond);\n      this.queryObject[this.paramNames[cond]] = n;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/crud-request/src/request-query.parser.ts",
    "content": "import {\n  hasLength,\n  hasValue,\n  isString,\n  isArrayFull,\n  isDate,\n  isDateString,\n  isObject,\n  isStringFull,\n  objKeys,\n  isNil,\n  ObjectLiteral,\n} from '@nestjsx/util';\nimport { ClassTransformOptions } from 'class-transformer';\n\nimport { RequestQueryException } from './exceptions';\nimport { ParamsOptions, ParsedRequestParams, RequestQueryBuilderOptions } from './interfaces';\nimport { RequestQueryBuilder } from './request-query.builder';\nimport {\n  validateCondition,\n  validateJoin,\n  validateNumeric,\n  validateParamOption,\n  validateSort,\n  validateUUID,\n} from './request-query.validator';\nimport {\n  ComparisonOperator,\n  QueryFields,\n  QueryFilter,\n  QueryJoin,\n  QuerySort,\n  SCondition,\n  SConditionAND,\n  SFields,\n} from './types';\n\n// tslint:disable:variable-name ban-types\nexport class RequestQueryParser implements ParsedRequestParams {\n  public fields: QueryFields = [];\n\n  public paramsFilter: QueryFilter[] = [];\n\n  public authPersist: ObjectLiteral = undefined;\n\n  public classTransformOptions: ClassTransformOptions = undefined;\n\n  public search: SCondition;\n\n  public filter: QueryFilter[] = [];\n\n  public or: QueryFilter[] = [];\n\n  public join: QueryJoin[] = [];\n\n  public sort: QuerySort[] = [];\n\n  public limit: number;\n\n  public offset: number;\n\n  public page: number;\n\n  public cache: number;\n\n  public includeDeleted: number;\n\n  private _params: any;\n\n  private _query: any;\n\n  private _paramNames: string[];\n\n  private _paramsOptions: ParamsOptions;\n\n  private get _options(): RequestQueryBuilderOptions {\n    return RequestQueryBuilder.getOptions();\n  }\n\n  static create(): RequestQueryParser {\n    return new RequestQueryParser();\n  }\n\n  getParsed(): ParsedRequestParams {\n    return {\n      fields: this.fields,\n      paramsFilter: this.paramsFilter,\n      authPersist: this.authPersist,\n      classTransformOptions: this.classTransformOptions,\n      search: this.search,\n      filter: this.filter,\n      or: this.or,\n      join: this.join,\n      sort: this.sort,\n      limit: this.limit,\n      offset: this.offset,\n      page: this.page,\n      cache: this.cache,\n      includeDeleted: this.includeDeleted,\n    };\n  }\n\n  parseQuery(query: any): this {\n    if (isObject(query)) {\n      const paramNames = objKeys(query);\n\n      if (hasLength(paramNames)) {\n        this._query = query;\n        this._paramNames = paramNames;\n        const searchData = this._query[this.getParamNames('search')[0]];\n        this.search = this.parseSearchQueryParam(searchData) as any;\n        if (isNil(this.search)) {\n          this.filter = this.parseQueryParam('filter', this.conditionParser.bind(this, 'filter'));\n          this.or = this.parseQueryParam('or', this.conditionParser.bind(this, 'or'));\n        }\n        this.fields = this.parseQueryParam('fields', this.fieldsParser.bind(this))[0] || [];\n        this.join = this.parseQueryParam('join', this.joinParser.bind(this));\n        this.sort = this.parseQueryParam('sort', this.sortParser.bind(this));\n        this.limit = this.parseQueryParam('limit', this.numericParser.bind(this, 'limit'))[0];\n        this.offset = this.parseQueryParam('offset', this.numericParser.bind(this, 'offset'))[0];\n        this.page = this.parseQueryParam('page', this.numericParser.bind(this, 'page'))[0];\n        this.cache = this.parseQueryParam('cache', this.numericParser.bind(this, 'cache'))[0];\n        this.includeDeleted = this.parseQueryParam(\n          'includeDeleted',\n          this.numericParser.bind(this, 'includeDeleted'),\n        )[0];\n      }\n    }\n\n    return this;\n  }\n\n  parseParams(params: any, options: ParamsOptions): this {\n    if (isObject(params)) {\n      const paramNames = objKeys(params);\n\n      if (hasLength(paramNames)) {\n        this._params = params;\n        this._paramsOptions = options;\n        this.paramsFilter = paramNames.map((name) => this.paramParser(name)).filter((filter) => filter);\n      }\n    }\n\n    return this;\n  }\n\n  setAuthPersist(persist: ObjectLiteral = {}) {\n    this.authPersist = persist || /* istanbul ignore next */ {};\n  }\n\n  setClassTransformOptions(options: ClassTransformOptions = {}) {\n    this.classTransformOptions = options || /* istanbul ignore next */ {};\n  }\n\n  convertFilterToSearch(filter: QueryFilter): SFields | SConditionAND {\n    const isEmptyValue = {\n      isnull: true,\n      notnull: true,\n    };\n\n    return filter\n      ? {\n          [filter.field]: {\n            [filter.operator]: isEmptyValue[filter.operator] ? isEmptyValue[filter.operator] : filter.value,\n          },\n        }\n      : /* istanbul ignore next */ {};\n  }\n\n  private getParamNames(type: keyof RequestQueryBuilderOptions['paramNamesMap']): string[] {\n    return this._paramNames.filter((p) => {\n      const name = this._options.paramNamesMap[type];\n      return isString(name) ? name === p : (name as string[]).some((m) => m === p);\n    });\n  }\n\n  private getParamValues(value: string | string[], parser: any): string[] {\n    if (isStringFull(value)) {\n      return [parser.call(this, value)];\n    }\n\n    if (isArrayFull(value)) {\n      return (value as string[]).map((val) => parser(val));\n    }\n\n    return [];\n  }\n\n  private parseQueryParam(type: keyof RequestQueryBuilderOptions['paramNamesMap'], parser: any) {\n    const param = this.getParamNames(type);\n\n    if (isArrayFull(param)) {\n      return param.reduce((a, name) => [...a, ...this.getParamValues(this._query[name], parser)], []);\n    }\n\n    return [];\n  }\n\n  private parseValue(val: any) {\n    try {\n      const parsed = JSON.parse(val);\n\n      if (!isDate(parsed) && isObject(parsed)) {\n        // throw new Error('Don\\'t support object now');\n        return val;\n      } else if (typeof parsed === 'number' && parsed.toLocaleString('fullwide', { useGrouping: false }) !== val) {\n        // JS cannot handle big numbers. Leave it as a string to prevent data loss\n        return val;\n      }\n\n      return parsed;\n    } catch (ignored) {\n      if (isDateString(val)) {\n        return new Date(val);\n      }\n\n      return val;\n    }\n  }\n\n  private parseValues(vals: any) {\n    if (isArrayFull(vals)) {\n      return vals.map((v: any) => this.parseValue(v));\n    } else {\n      return this.parseValue(vals);\n    }\n  }\n\n  private fieldsParser(data: string): QueryFields {\n    return data.split(this._options.delimStr);\n  }\n\n  private parseSearchQueryParam(d: any): SCondition {\n    try {\n      if (isNil(d)) {\n        return undefined;\n      }\n\n      const data = JSON.parse(d);\n\n      if (!isObject(data)) {\n        throw new Error();\n      }\n\n      return data;\n    } catch (_) {\n      throw new RequestQueryException('Invalid search param. JSON expected');\n    }\n  }\n\n  private conditionParser(cond: 'filter' | 'or' | 'search', data: string): QueryFilter {\n    const isArrayValue = ['in', 'notin', 'between', '$in', '$notin', '$between', '$inL', '$notinL'];\n    const isEmptyValue = ['isnull', 'notnull', '$isnull', '$notnull'];\n    const param = data.split(this._options.delim);\n    const field = param[0];\n    const operator = param[1] as ComparisonOperator;\n    let value = param[2] || '';\n\n    if (isArrayValue.some((name) => name === operator)) {\n      value = value.split(this._options.delimStr) as any;\n    }\n\n    value = this.parseValues(value);\n\n    if (!isEmptyValue.some((name) => name === operator) && !hasValue(value)) {\n      throw new RequestQueryException(`Invalid ${cond} value`);\n    }\n\n    const condition: QueryFilter = { field, operator, value };\n    validateCondition(condition, cond);\n\n    return condition;\n  }\n\n  private joinParser(data: string): QueryJoin {\n    const param = data.split(this._options.delim);\n    const join: QueryJoin = {\n      field: param[0],\n      select: isStringFull(param[1]) ? param[1].split(this._options.delimStr) : undefined,\n    };\n    validateJoin(join);\n\n    return join;\n  }\n\n  private sortParser(data: string): QuerySort {\n    const param = data.split(this._options.delimStr);\n    const sort: QuerySort = {\n      field: param[0],\n      order: param[1] as any,\n    };\n    validateSort(sort);\n\n    return sort;\n  }\n\n  private numericParser(num: 'limit' | 'offset' | 'page' | 'cache' | 'includeDeleted', data: string): number {\n    const val = this.parseValue(data);\n    validateNumeric(val, num);\n\n    return val;\n  }\n\n  private paramParser(name: string): QueryFilter {\n    validateParamOption(this._paramsOptions, name);\n    const option = this._paramsOptions[name];\n\n    if (option.disabled) {\n      return undefined;\n    }\n\n    let value = this._params[name];\n\n    switch (option.type) {\n      case 'number':\n        value = this.parseValue(value);\n        validateNumeric(value, `param ${name}`);\n        break;\n      case 'uuid':\n        validateUUID(value, name);\n        break;\n      default:\n        break;\n    }\n\n    return { field: option.field, operator: '$eq', value };\n  }\n}\n"
  },
  {
    "path": "packages/crud-request/src/request-query.validator.ts",
    "content": "import { isUndefined, isArrayStrings, isStringFull, isObject, isEqual, isNumber, isNil, objKeys } from '@nestjsx/util';\n\nimport { RequestQueryException } from './exceptions';\nimport { ParamsOptions, ParamOption } from './interfaces';\nimport { QueryFields, QueryFilter, ComparisonOperator, QueryJoin, QuerySort, CondOperator } from './types';\n\nexport const deprecatedComparisonOperatorsList = [\n  'eq',\n  'ne',\n  'gt',\n  'lt',\n  'gte',\n  'lte',\n  'starts',\n  'ends',\n  'cont',\n  'excl',\n  'in',\n  'notin',\n  'isnull',\n  'notnull',\n  'between',\n];\nexport const comparisonOperatorsList = [\n  ...deprecatedComparisonOperatorsList,\n  ...objKeys(CondOperator).map((n) => CondOperator[n]),\n];\n\nexport const sortOrdersList = ['ASC', 'DESC'];\n\nconst comparisonOperatorsListStr = comparisonOperatorsList.join();\nconst sortOrdersListStr = sortOrdersList.join();\n\nexport function validateFields(fields: QueryFields): void {\n  if (!isArrayStrings(fields)) {\n    throw new RequestQueryException('Invalid fields. Array of strings expected');\n  }\n}\n\nexport function validateCondition(val: QueryFilter, cond: 'filter' | 'or' | 'search'): void {\n  if (!isObject(val) || !isStringFull(val.field)) {\n    throw new RequestQueryException(`Invalid field type in ${cond} condition. String expected`);\n  }\n  validateComparisonOperator(val.operator);\n}\n\nexport function validateComparisonOperator(operator: ComparisonOperator): void {\n  if (!comparisonOperatorsList.includes(operator)) {\n    throw new RequestQueryException(`Invalid comparison operator. ${comparisonOperatorsListStr} expected`);\n  }\n}\n\nexport function validateJoin(join: QueryJoin): void {\n  if (!isObject(join) || !isStringFull(join.field)) {\n    throw new RequestQueryException('Invalid join field. String expected');\n  }\n  if (!isUndefined(join.select) && !isArrayStrings(join.select)) {\n    throw new RequestQueryException('Invalid join select. Array of strings expected');\n  }\n}\n\nexport function validateSort(sort: QuerySort): void {\n  if (!isObject(sort) || !isStringFull(sort.field)) {\n    throw new RequestQueryException('Invalid sort field. String expected');\n  }\n  if (!isEqual(sort.order, sortOrdersList[0]) && !isEqual(sort.order, sortOrdersList[1])) {\n    throw new RequestQueryException(`Invalid sort order. ${sortOrdersListStr} expected`);\n  }\n}\n\nexport function validateNumeric(\n  val: number,\n  num: 'limit' | 'offset' | 'page' | 'cache' | 'include_deleted' | string,\n): void {\n  if (!isNumber(val)) {\n    throw new RequestQueryException(`Invalid ${num}. Number expected`);\n  }\n}\n\nexport function validateParamOption(options: ParamsOptions, name: string) {\n  if (!isObject(options)) {\n    throw new RequestQueryException(`Invalid param ${name}. Invalid crud options`);\n  }\n  const option = options[name];\n  if (option && option.disabled) {\n    return;\n  }\n  if (!isObject(option) || isNil(option.field) || isNil(option.type)) {\n    throw new RequestQueryException('Invalid param option in Crud');\n  }\n}\n\nexport function validateUUID(str: string, name: string) {\n  const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n  const uuidV4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n  if (!uuidV4.test(str) && !uuid.test(str)) {\n    throw new RequestQueryException(`Invalid param ${name}. UUID string expected`);\n  }\n}\n"
  },
  {
    "path": "packages/crud-request/src/types/index.ts",
    "content": "export * from './request-query.types';\nexport * from './request-param.types';\n"
  },
  {
    "path": "packages/crud-request/src/types/request-param.types.ts",
    "content": "export type ParamOptionType = 'number' | 'string' | 'uuid';\n"
  },
  {
    "path": "packages/crud-request/src/types/request-query.types.ts",
    "content": "export type QueryFields = string[];\n\nexport type QueryFilter = {\n  field: string;\n  operator: ComparisonOperator;\n  value?: any;\n};\n\nexport type QueryFilterArr = [string, ComparisonOperator, any?];\n\nexport type QueryJoin = {\n  field: string;\n  select?: QueryFields;\n};\n\nexport type QueryJoinArr = [string, QueryFields?];\n\nexport type QuerySort = {\n  field: string;\n  order: QuerySortOperator;\n};\n\nexport type QuerySortArr = [string, QuerySortOperator];\n\nexport type QuerySortOperator = 'ASC' | 'DESC';\n\ntype DeprecatedCondOperator =\n  | 'eq'\n  | 'ne'\n  | 'gt'\n  | 'lt'\n  | 'gte'\n  | 'lte'\n  | 'starts'\n  | 'ends'\n  | 'cont'\n  | 'excl'\n  | 'in'\n  | 'notin'\n  | 'isnull'\n  | 'notnull'\n  | 'between';\n\nexport enum CondOperator {\n  EQUALS = '$eq',\n  NOT_EQUALS = '$ne',\n  GREATER_THAN = '$gt',\n  LOWER_THAN = '$lt',\n  GREATER_THAN_EQUALS = '$gte',\n  LOWER_THAN_EQUALS = '$lte',\n  STARTS = '$starts',\n  ENDS = '$ends',\n  CONTAINS = '$cont',\n  EXCLUDES = '$excl',\n  IN = '$in',\n  NOT_IN = '$notin',\n  IS_NULL = '$isnull',\n  NOT_NULL = '$notnull',\n  BETWEEN = '$between',\n  EQUALS_LOW = '$eqL',\n  NOT_EQUALS_LOW = '$neL',\n  STARTS_LOW = '$startsL',\n  ENDS_LOW = '$endsL',\n  CONTAINS_LOW = '$contL',\n  EXCLUDES_LOW = '$exclL',\n  IN_LOW = '$inL',\n  NOT_IN_LOW = '$notinL',\n}\n\nexport type ComparisonOperator = DeprecatedCondOperator | keyof SFieldOperator;\n\n// new search\nexport type SPrimitivesVal = string | number | boolean;\n\nexport type SFiledValues = SPrimitivesVal | Array<SPrimitivesVal>;\n\nexport type SFieldOperator = {\n  $eq?: SFiledValues;\n  $ne?: SFiledValues;\n  $gt?: SFiledValues;\n  $lt?: SFiledValues;\n  $gte?: SFiledValues;\n  $lte?: SFiledValues;\n  $starts?: SFiledValues;\n  $ends?: SFiledValues;\n  $cont?: SFiledValues;\n  $excl?: SFiledValues;\n  $in?: SFiledValues;\n  $notin?: SFiledValues;\n  $between?: SFiledValues;\n  $isnull?: SFiledValues;\n  $notnull?: SFiledValues;\n  $eqL?: SFiledValues;\n  $neL?: SFiledValues;\n  $startsL?: SFiledValues;\n  $endsL?: SFiledValues;\n  $contL?: SFiledValues;\n  $exclL?: SFiledValues;\n  $inL?: SFiledValues;\n  $notinL?: SFiledValues;\n  $or?: SFieldOperator;\n  $and?: never;\n};\n\nexport type SField = SPrimitivesVal | SFieldOperator;\n\nexport type SFields = {\n  [key: string]: SField | Array<SFields | SConditionAND> | undefined;\n  $or?: Array<SFields | SConditionAND>;\n  $and?: never;\n};\n\nexport type SConditionAND = {\n  $and?: Array<SFields | SConditionAND>;\n  $or?: never;\n};\n\nexport type SConditionKey = '$and' | '$or';\n\nexport type SCondition = SFields | SConditionAND;\n"
  },
  {
    "path": "packages/crud-request/test/request-query.builder.spec.ts",
    "content": "import 'jest-extended';\nimport { RequestQueryBuilder } from '../src/request-query.builder';\nimport { RequestQueryException } from '../src/exceptions/request-query.exception';\n\nconst defaultOptions = { ...(RequestQueryBuilder as any)._options };\n\ndescribe('#request-query', () => {\n  describe('#RequestQueryBuilder', () => {\n    let qb: RequestQueryBuilder;\n\n    beforeEach(() => {\n      qb = RequestQueryBuilder.create();\n    });\n\n    afterEach(() => {\n      (RequestQueryBuilder as any)._options = defaultOptions;\n    });\n\n    it('should be a function', () => {\n      expect(typeof RequestQueryBuilder).toBe('function');\n    });\n\n    describe('#static setOptions', () => {\n      it('should merge options, 1', () => {\n        const override = 'override';\n        RequestQueryBuilder.setOptions({\n          paramNamesMap: { fields: [override] },\n        });\n        const paramNamesMap = (RequestQueryBuilder as any)._options.paramNamesMap;\n        expect(paramNamesMap.fields[0]).toBe(override);\n        expect(paramNamesMap.page).toBe('page');\n      });\n      it('should merge options, 2', () => {\n        const override = 'override';\n        RequestQueryBuilder.setOptions({ delim: override });\n        const _options = (RequestQueryBuilder as any)._options;\n        expect(_options.delim).toBe(override);\n      });\n    });\n\n    describe('#select', () => {\n      it('should not throw', () => {\n        (qb as any).select();\n        expect(qb.queryObject.fields).toBeUndefined();\n      });\n      it('should throw an error', () => {\n        expect((qb.select as any).bind(qb, [false])).toThrowError(RequestQueryException);\n      });\n      it('should set fields', () => {\n        qb.select(['foo', 'bar']);\n        const expected = 'foo,bar';\n        expect(qb.queryObject.fields).toBe(expected);\n      });\n    });\n\n    describe('#setFilter', () => {\n      it('should not throw', () => {\n        (qb as any).setFilter();\n        expect(qb.queryObject.filter).toBeUndefined();\n      });\n      it('should throw an error, 1', () => {\n        expect((qb.setFilter as any).bind(qb, { field: 1 })).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 2', () => {\n        expect((qb.setFilter as any).bind(qb, { field: 'foo', operator: 'bar' })).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 3', () => {\n        expect((qb.setFilter as any).bind(qb, [{}])).toThrowError(RequestQueryException);\n      });\n      it('should set filter, 1', () => {\n        qb.setFilter({ field: 'foo', operator: 'eq', value: 'bar' });\n        const expected = ['foo||eq||bar'];\n        expect(qb.queryObject.filter).toIncludeSameMembers(expected);\n      });\n      it('should set filter, 2', () => {\n        qb.setFilter([\n          { field: 'foo', operator: 'eq', value: 'bar' },\n          { field: 'baz', operator: 'ne', value: 'zoo' },\n        ]);\n        const expected = ['foo||eq||bar', 'baz||ne||zoo'];\n        expect(qb.queryObject.filter).toIncludeSameMembers(expected);\n      });\n      it('should set filter, 3', () => {\n        qb.setFilter([['foo', 'eq', 'bar'], { field: 'baz', operator: 'ne', value: 'zoo' }]);\n        const expected = ['foo||eq||bar', 'baz||ne||zoo'];\n        expect(qb.queryObject.filter).toIncludeSameMembers(expected);\n      });\n      it('should set filter, 4', () => {\n        qb.setFilter([\n          ['foo', 'eq', 'bar'],\n          ['baz', 'ne', 'zoo'],\n        ]);\n        const expected = ['foo||eq||bar', 'baz||ne||zoo'];\n        expect(qb.queryObject.filter).toIncludeSameMembers(expected);\n      });\n      it('should set filter, 5', () => {\n        qb.setFilter(['foo', 'eq', 'bar']);\n        const expected = ['foo||eq||bar'];\n        expect(qb.queryObject.filter).toIncludeSameMembers(expected);\n      });\n    });\n\n    describe('#setOr', () => {\n      it('should not throw', () => {\n        (qb as any).setOr();\n        expect(qb.queryObject.or).toBeUndefined();\n      });\n      it('should throw an error, 1', () => {\n        expect((qb.setOr as any).bind(qb, { field: 1 })).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 2', () => {\n        expect((qb.setOr as any).bind(qb, { field: 'foo', operator: 'bar' })).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 3', () => {\n        expect((qb.setOr as any).bind(qb, [{}])).toThrowError(RequestQueryException);\n      });\n      it('should set or, 1', () => {\n        qb.setOr({ field: 'foo', operator: 'eq', value: 'bar' });\n        const expected = ['foo||eq||bar'];\n        expect(qb.queryObject.or).toIncludeSameMembers(expected);\n      });\n      it('should set or, 2', () => {\n        qb.setOr([\n          { field: 'foo', operator: 'eq', value: 'bar' },\n          { field: 'baz', operator: 'ne', value: 'zoo' },\n        ]);\n        const expected = ['foo||eq||bar', 'baz||ne||zoo'];\n        expect(qb.queryObject.or).toIncludeSameMembers(expected);\n      });\n    });\n\n    describe('#setJoin', () => {\n      it('should not throw', () => {\n        (qb as any).setJoin();\n        expect(qb.queryObject.join).toBeUndefined();\n      });\n      it('should throw an error, 1', () => {\n        expect((qb.setJoin as any).bind(qb, { field: 1 })).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 2', () => {\n        expect((qb.setJoin as any).bind(qb, { field: 'foo', select: 1 })).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 3', () => {\n        expect((qb.setJoin as any).bind(qb, [{}])).toThrowError(RequestQueryException);\n      });\n      it('should set join, 1', () => {\n        qb.setJoin({ field: 'foo' });\n        const expected = ['foo'];\n        expect(qb.queryObject.join).toIncludeSameMembers(expected);\n      });\n      it('should set join, 2', () => {\n        qb.setJoin([{ field: 'foo' }, { field: 'bar', select: ['a', 'b', 'c'] }]);\n        const expected = ['foo', 'bar||a,b,c'];\n        expect(qb.queryObject.join).toIncludeSameMembers(expected);\n      });\n      it('should set join, 3', () => {\n        qb.setJoin(['foo']);\n        const expected = ['foo'];\n        expect(qb.queryObject.join).toIncludeSameMembers(expected);\n      });\n      it('should set join, 4', () => {\n        qb.setJoin(['foo', ['a', 'b', 'c']]);\n        const expected = ['foo||a,b,c'];\n        expect(qb.queryObject.join).toIncludeSameMembers(expected);\n      });\n      it('should set join, 5', () => {\n        qb.setJoin([{ field: 'baz' }, ['foo', ['a', 'b', 'c']]]);\n        const expected = ['baz', 'foo||a,b,c'];\n        expect(qb.queryObject.join).toIncludeSameMembers(expected);\n      });\n    });\n\n    describe('#sortBy', () => {\n      it('should not throw', () => {\n        (qb as any).sortBy();\n        expect(qb.queryObject.sort).toBeUndefined();\n      });\n      it('should throw an error, 1', () => {\n        expect((qb.sortBy as any).bind(qb, { field: 1 })).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 2', () => {\n        expect((qb.sortBy as any).bind(qb, { field: 'foo', order: 'bar' })).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 3', () => {\n        expect((qb.sortBy as any).bind(qb, [{}])).toThrowError(RequestQueryException);\n      });\n      it('should set sort, 1', () => {\n        qb.sortBy({ field: 'foo', order: 'ASC' });\n        const expected = ['foo,ASC'];\n        expect(qb.queryObject.sort).toIncludeSameMembers(expected);\n      });\n      it('should set sort, 2', () => {\n        qb.sortBy([\n          { field: 'foo', order: 'ASC' },\n          { field: 'bar', order: 'DESC' },\n        ]);\n        const expected = ['foo,ASC', 'bar,DESC'];\n        expect(qb.queryObject.sort).toIncludeSameMembers(expected);\n      });\n      it('should set sort, 3', () => {\n        qb.sortBy(['foo', 'ASC']);\n        const expected = ['foo,ASC'];\n        expect(qb.queryObject.sort).toIncludeSameMembers(expected);\n      });\n      it('should set sort, 4', () => {\n        qb.sortBy([['foo', 'ASC']]);\n        const expected = ['foo,ASC'];\n        expect(qb.queryObject.sort).toIncludeSameMembers(expected);\n      });\n      it('should set sort, 5', () => {\n        qb.sortBy([{ field: 'bar', order: 'DESC' }, ['foo', 'ASC']]);\n        const expected = ['bar,DESC', 'foo,ASC'];\n        expect(qb.queryObject.sort).toIncludeSameMembers(expected);\n      });\n    });\n\n    describe('#setLimit', () => {\n      it('should not throw', () => {\n        (qb as any).setLimit();\n        expect(qb.queryObject.limit).toBeUndefined();\n      });\n      it('should throw an error', () => {\n        expect((qb.setLimit as any).bind(qb, {})).toThrowError(RequestQueryException);\n      });\n      it('should set limit', () => {\n        const expected = 10;\n        qb.setLimit(expected);\n        expect(qb.queryObject.limit).toBe(expected);\n      });\n    });\n\n    describe('#setOffset', () => {\n      it('should not throw', () => {\n        (qb as any).setOffset();\n        expect(qb.queryObject.offset).toBeUndefined();\n      });\n      it('should throw an error', () => {\n        expect((qb.setOffset as any).bind(qb, {})).toThrowError(RequestQueryException);\n      });\n      it('should set offset', () => {\n        const expected = 10;\n        qb.setOffset(expected);\n        expect(qb.queryObject.offset).toBe(expected);\n      });\n    });\n\n    describe('#setPage', () => {\n      it('should not throw', () => {\n        (qb as any).setPage();\n        expect(qb.queryObject.page).toBeUndefined();\n      });\n      it('should throw an error', () => {\n        expect((qb.setPage as any).bind(qb, {})).toThrowError(RequestQueryException);\n      });\n      it('should set page', () => {\n        const expected = 10;\n        qb.setPage(expected);\n        expect(qb.queryObject.page).toBe(expected);\n      });\n    });\n\n    describe('#resetCache', () => {\n      it('should set cache', () => {\n        expect(qb.queryObject.cache).toBeUndefined();\n        qb.resetCache();\n        expect(qb.queryObject.cache).toBe(0);\n      });\n    });\n\n    describe('#cond', () => {\n      it('should throw an error, 1', () => {\n        expect(qb.cond as any).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 2', () => {\n        expect((qb.cond as any).bind(qb, {})).toThrowError(RequestQueryException);\n      });\n      it('should return a filter string from an object', () => {\n        const test = qb.cond({ field: 'foo', operator: 'eq', value: 'bar' });\n        const expected = 'foo||eq||bar';\n        expect(test).toBe(expected);\n      });\n      it('should return a filter string from an array', () => {\n        const test = qb.cond(['foo', 'eq', 'bar']);\n        const expected = 'foo||eq||bar';\n        expect(test).toBe(expected);\n      });\n    });\n\n    describe('#query', () => {\n      it('should return an empty string', () => {\n        expect(qb.query()).toBe('');\n      });\n      it('should return query with overrided fields name', () => {\n        RequestQueryBuilder.setOptions({ paramNamesMap: { fields: ['override'] } });\n        qb.setParamNames();\n        const test = qb.select(['foo', 'bar']).query();\n        const test2 = qb.select(['foo', 'bar']).query(false);\n        const expected = 'override=foo%2Cbar';\n        const expected2 = 'override=foo,bar';\n        expect(test).toBe(expected);\n        expect(test2).toBe(expected2);\n      });\n      it('should return valid query string with filters', () => {\n        const test = qb\n          .select(['foo', 'bar'])\n          .setFilter([\n            { field: 'is', operator: 'notnull' },\n            { field: 'foo', operator: 'lt', value: 10 },\n          ])\n          .query(false);\n        const expected = 'fields=foo,bar&filter[0]=is||notnull&filter[1]=foo||lt||10';\n        expect(test).toBe(expected);\n      });\n      it('should return a valid query string', () => {\n        const test = qb\n          .select(['foo', 'bar'])\n          .setFilter(['is', 'notnull'])\n          .setOr({ field: 'ok', operator: 'ne', value: false })\n          .setJoin({ field: 'voo', select: ['h', 'data'] })\n          .setLimit(1)\n          .setOffset(2)\n          .setPage(3)\n          .sortBy({ field: 'foo', order: 'DESC' })\n          .resetCache()\n          .setIncludeDeleted(1)\n          .query(false);\n        const expected =\n          'fields=foo,bar&filter[0]=is||notnull&or[0]=ok||ne||false&join[0]=voo||h,data&limit=1&offset=2&page=3&sort[0]=foo,DESC&cache=0&include_deleted=1'; // eslint-disable-line\n        expect(test).toBe(expected);\n      });\n    });\n\n    describe('#search', () => {\n      it('should not throw, 1', () => {\n        (qb as any).search();\n        expect(qb.queryObject.search).toBeUndefined();\n      });\n      it('should not throw, 2', () => {\n        (qb as any).search(false);\n        expect(qb.queryObject.search).toBeUndefined();\n      });\n      it('should set search string, 1', () => {\n        const test = qb.search({ $or: [{ id: 1 }, { name: 'foo' }] }).query(false);\n        const expected = 's={\"$or\":[{\"id\":1},{\"name\":\"foo\"}]}';\n        expect(test).toBe(expected);\n      });\n      it('should set search string, 2', () => {\n        const test = qb.search({ $or: [{ id: 1 }, { name: 'foo' }] }).query();\n        const expected = 's=%7B%22%24or%22%3A%5B%7B%22id%22%3A1%7D%2C%7B%22name%22%3A%22foo%22%7D%5D%7D';\n        expect(test).toBe(expected);\n      });\n    });\n\n    describe('#createFromParams', () => {\n      it('should return an empty query string', () => {\n        const test = RequestQueryBuilder.create().query();\n        expect(test).toBe('');\n      });\n      it('should return a valid query string, 1', () => {\n        const test = RequestQueryBuilder.create({\n          fields: ['foo', 'bar'],\n          filter: ['is', 'notnull'],\n          or: { field: 'ok', operator: 'ne', value: false },\n          join: { field: 'voo', select: ['h', 'data'] },\n          limit: 1,\n          offset: 2,\n          page: 3,\n          sort: [['foo', 'DESC']],\n          resetCache: true,\n        }).query(false);\n        const expected =\n          'fields=foo,bar&filter[0]=is||notnull&or[0]=ok||ne||false&join[0]=voo||h,data&limit=1&offset=2&page=3&sort[0]=foo,DESC&cache=0';\n        expect(test).toBe(expected);\n      });\n      it('should return a valid query string, 2', () => {\n        const test = RequestQueryBuilder.create({\n          fields: ['foo', 'bar'],\n        }).query(false);\n        const expected = 'fields=foo,bar';\n        expect(test).toBe(expected);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud-request/test/request-query.parser.spec.ts",
    "content": "import 'jest-extended';\nimport { RequestQueryException } from '../src/exceptions/request-query.exception';\nimport { ParamsOptions, ParsedRequestParams } from '../src/interfaces';\nimport { RequestQueryParser } from '../src/request-query.parser';\nimport { QueryFilter, QueryJoin, QuerySort } from '../src/types';\n\ndescribe('#request-query', () => {\n  describe('RequestQueryParser', () => {\n    let qp: RequestQueryParser;\n\n    beforeEach(() => {\n      qp = RequestQueryParser.create();\n    });\n\n    describe('#parseQury', () => {\n      it('should return instance of RequestQueryParse', () => {\n        expect((qp as any).parseQuery()).toBeInstanceOf(RequestQueryParser);\n        expect((qp as any).parseQuery({})).toBeInstanceOf(RequestQueryParser);\n      });\n\n      describe('#parse fields', () => {\n        it('should set empty array, 1', () => {\n          const query = { select: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.fields).toMatchObject(expected);\n        });\n        it('should set empty array, 2', () => {\n          const query = { foo: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.fields).toMatchObject(expected);\n        });\n        it('should set array, 1', () => {\n          const query = { select: 'foo' };\n          const expected = ['foo'];\n          const test = qp.parseQuery(query);\n          expect(test.fields).toMatchObject(expected);\n        });\n        it('should set array, 2', () => {\n          const query = { select: 'foo,bar' };\n          const expected = ['foo', 'bar'];\n          const test = qp.parseQuery(query);\n          expect(test.fields).toMatchObject(expected);\n        });\n      });\n\n      describe('#parse filter', () => {\n        it('should set empty array, 1', () => {\n          const query = { filter: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.filter).toMatchObject(expected);\n        });\n        it('should set empty array, 2', () => {\n          const query = { foo: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.filter).toMatchObject(expected);\n        });\n        it('should throw an error, 1', () => {\n          const query = { filter: 'foo||invalid||bar' };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should throw an error, 2', () => {\n          const query = { filter: 'foo||eq' };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should set array, 1', () => {\n          const query = { filter: 'foo||eq||bar' };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'eq', value: 'bar' }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set array, 2', () => {\n          const query = { filter: ['foo||eq||bar', 'baz||ne||boo'] };\n          const expected: QueryFilter[] = [\n            { field: 'foo', operator: 'eq', value: 'bar' },\n            { field: 'baz', operator: 'ne', value: 'boo' },\n          ];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n          expect(test.filter[1]).toMatchObject(expected[1]);\n        });\n        it('should set array, 3', () => {\n          const query = { filter: ['foo||in||1,2'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'in', value: [1, 2] }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set array, 4', () => {\n          const query = { filter: ['foo||isnull'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'isnull', value: '' }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set array, 5', () => {\n          const query = { filter: ['foo||eq||{\"foo\":true}'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'eq', value: '{\"foo\":true}' }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set array, 6', () => {\n          const query = { filter: ['foo||eq||1'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'eq', value: 1 }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set date, 7', () => {\n          const now = new Date();\n          const query = { filter: [`foo||eq||${now.toJSON()}`] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'eq', value: now }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set false, 8', () => {\n          const query = { filter: ['foo||eq||false'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'eq', value: false }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set true, 9', () => {\n          const query = { filter: ['foo||eq||true'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'eq', value: true }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set number, 10', () => {\n          const query = { filter: ['foo||eq||12345'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'eq', value: 12345 }];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n        it('should set string, 11', () => {\n          const query = { filter: ['foo||eq||4202140192612927005304000000236630'] };\n          const expected: QueryFilter[] = [\n            { field: 'foo', operator: 'eq', value: '4202140192612927005304000000236630' },\n          ];\n          const test = qp.parseQuery(query);\n          expect(test.filter[0]).toMatchObject(expected[0]);\n        });\n      });\n\n      describe('#parse or', () => {\n        it('should set empty array, 1', () => {\n          const query = { or: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.or).toMatchObject(expected);\n        });\n        it('should set empty array, 2', () => {\n          const query = { foo: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.or).toMatchObject(expected);\n        });\n        it('should throw an error, 1', () => {\n          const query = { or: 'foo||invalid||bar' };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should throw an error, 2', () => {\n          const query = { or: 'foo||eq' };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should set array, 1', () => {\n          const query = { or: 'foo||eq||bar' };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'eq', value: 'bar' }];\n          const test = qp.parseQuery(query);\n          expect(test.or[0]).toMatchObject(expected[0]);\n        });\n        it('should set array, 2', () => {\n          const query = { or: ['foo||eq||bar', 'baz||ne||boo'] };\n          const expected: QueryFilter[] = [\n            { field: 'foo', operator: 'eq', value: 'bar' },\n            { field: 'baz', operator: 'ne', value: 'boo' },\n          ];\n          const test = qp.parseQuery(query);\n          expect(test.or[0]).toMatchObject(expected[0]);\n          expect(test.or[1]).toMatchObject(expected[1]);\n        });\n        it('should set array, 3', () => {\n          const query = { or: ['foo||in||1,2'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'in', value: [1, 2] }];\n          const test = qp.parseQuery(query);\n          expect(test.or[0]).toMatchObject(expected[0]);\n        });\n        it('should set array, 4', () => {\n          const query = { or: ['foo||isnull'] };\n          const expected: QueryFilter[] = [{ field: 'foo', operator: 'isnull', value: '' }];\n          const test = qp.parseQuery(query);\n          expect(test.or[0]).toMatchObject(expected[0]);\n        });\n      });\n\n      describe('#parse join', () => {\n        it('should set empty array, 1', () => {\n          const query = { join: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.join).toMatchObject(expected);\n        });\n        it('should set empty array, 2', () => {\n          const query = { foo: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.join).toMatchObject(expected);\n        });\n        it('should set array, 1', () => {\n          const query = { join: 'foo' };\n          const expected: QueryJoin[] = [{ field: 'foo' }];\n          const test = qp.parseQuery(query);\n          expect(test.join[0]).toMatchObject(expected[0]);\n        });\n        it('should set array, 2', () => {\n          const query = { join: ['foo', 'bar||baz,boo'] };\n          const expected: QueryJoin[] = [{ field: 'foo' }, { field: 'bar', select: ['baz', 'boo'] }];\n          const test = qp.parseQuery(query);\n          expect(test.join[0]).toMatchObject(expected[0]);\n          expect(test.join[1]).toMatchObject(expected[1]);\n        });\n      });\n\n      describe('#parse sort', () => {\n        it('should set empty array, 1', () => {\n          const query = { sort: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.sort).toMatchObject(expected);\n        });\n        it('should set empty array, 2', () => {\n          const query = { foo: '' };\n          const expected = [];\n          const test = qp.parseQuery(query);\n          expect(test.sort).toMatchObject(expected);\n        });\n        it('should throw an error, 1', () => {\n          const query = { sort: 'foo' };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should throw an error, 2', () => {\n          const query = { sort: 'foo,boo' };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should set array', () => {\n          const query = { sort: ['foo,ASC', 'bar,DESC'] };\n          const expected: QuerySort[] = [\n            { field: 'foo', order: 'ASC' },\n            { field: 'bar', order: 'DESC' },\n          ];\n          const test = qp.parseQuery(query);\n          expect(test.sort[0]).toMatchObject(expected[0]);\n          expect(test.sort[1]).toMatchObject(expected[1]);\n        });\n      });\n\n      describe('#parse limit', () => {\n        it('should set undefined, 1', () => {\n          const query = { limit: '' };\n          const test = qp.parseQuery(query);\n          expect(test.limit).toBeUndefined();\n        });\n        it('should set undefined, 2', () => {\n          const query = { foo: '' };\n          const test = qp.parseQuery(query);\n          expect(test.limit).toBeUndefined();\n        });\n        it('should throw an error', () => {\n          const query = { limit: 'a' };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should set value', () => {\n          const query = { limit: '10' };\n          const expected = 10;\n          const test = qp.parseQuery(query);\n          expect(test.limit).toBe(expected);\n        });\n      });\n\n      describe('#parse offset', () => {\n        it('should set undefined, 1', () => {\n          const query = { offset: '' };\n          const test = qp.parseQuery(query);\n          expect(test.offset).toBeUndefined();\n        });\n        it('should set undefined, 2', () => {\n          const query = { foo: '' };\n          const test = qp.parseQuery(query);\n          expect(test.offset).toBeUndefined();\n        });\n        it('should throw an error', () => {\n          const query = { offset: 'a' };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should set value', () => {\n          const query = { offset: '10' };\n          const expected = 10;\n          const test = qp.parseQuery(query);\n          expect(test.offset).toBe(expected);\n        });\n      });\n\n      describe('#parse page', () => {\n        it('should set undefined, 1', () => {\n          const query = { page: '' };\n          const test = qp.parseQuery(query);\n          expect(test.page).toBeUndefined();\n        });\n        it('should set undefined, 2', () => {\n          const query = { foo: '' };\n          const test = qp.parseQuery(query);\n          expect(test.page).toBeUndefined();\n        });\n        it('should throw an error', () => {\n          const query = { page: ['a'] };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should set value', () => {\n          const query = { page: ['10'] };\n          const expected = 10;\n          const test = qp.parseQuery(query);\n          expect(test.page).toBe(expected);\n        });\n      });\n\n      describe('#parse cache', () => {\n        it('should set undefined, 1', () => {\n          const query = { cache: '' };\n          const test = qp.parseQuery(query);\n          expect(test.cache).toBeUndefined();\n        });\n        it('should set undefined, 2', () => {\n          const query = { foo: '' };\n          const test = qp.parseQuery(query);\n          expect(test.cache).toBeUndefined();\n        });\n        it('should throw an error', () => {\n          const query = { cache: ['a'] };\n          expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n        });\n        it('should set value', () => {\n          const query = { cache: ['10'] };\n          const expected = 10;\n          const test = qp.parseQuery(query);\n          expect(test.cache).toBe(expected);\n        });\n      });\n    });\n\n    describe('#parse search', () => {\n      it('should set undefined', () => {\n        const query = { foo: '' };\n        const test = qp.parseQuery(query);\n        expect(test.search).toBeUndefined();\n      });\n      it('should throw an error, 1', () => {\n        const query = { s: 'invalid' };\n        expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 2', () => {\n        const query = { s: 'true' };\n        expect(qp.parseQuery.bind(qp, query)).toThrowError(RequestQueryException);\n      });\n      it('should parse search', () => {\n        const query = { s: '{\"$or\":[{\"id\":1},{\"name\":\"foo\"}]}' };\n        const expected = { $or: [{ id: 1 }, { name: 'foo' }] };\n        const test = qp.parseQuery(query);\n        expect(test.search).toMatchObject(expected);\n      });\n    });\n\n    describe('#parseParams', () => {\n      it('should return instance of RequestQueryParse', () => {\n        expect((qp as any).parseParams()).toBeInstanceOf(RequestQueryParser);\n        expect((qp as any).parseParams({})).toBeInstanceOf(RequestQueryParser);\n      });\n      it('should throw an error, 1', () => {\n        const params = { foo: 'bar' };\n        const options = undefined;\n        expect(qp.parseParams.bind(qp, params, options)).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 2', () => {\n        const params = { foo: 'bar' };\n        const options = {};\n        expect(qp.parseParams.bind(qp, params, options)).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 3', () => {\n        const params = { foo: 'bar' };\n        const options = { foo: {} };\n        expect(qp.parseParams.bind(qp, params, options)).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 4', () => {\n        const params = { foo: 'bar' };\n        const options = { foo: { field: 'number' } };\n        expect(qp.parseParams.bind(qp, params, options)).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 5', () => {\n        const params = { foo: 'bar' };\n        const options = { foo: { field: 'foo', type: 'number' } };\n        expect(qp.parseParams.bind(qp, params, options)).toThrowError(RequestQueryException);\n      });\n      it('should throw an error, 6', () => {\n        const params = { foo: 'bar' };\n        const options = { foo: { field: 'foo', type: 'uuid' } };\n        expect(qp.parseParams.bind(qp, params, options)).toThrowError(RequestQueryException);\n      });\n      it('should set paramsFilter', () => {\n        const params = {\n          foo: 'cb1751fd-7fcf-4eb5-b38e-86428b1fd88d',\n          bar: '1',\n          buz: 'string',\n          bigInt: '9007199254740999', // Bigger than Number.MAX_SAFE_INTEGER\n        };\n        const options: ParamsOptions = {\n          foo: { field: 'foo', type: 'uuid' },\n          bar: { field: 'bb', type: 'number' },\n          buz: { field: 'buz', type: 'string' },\n          bigInt: { field: 'bigInt', type: 'string' },\n        };\n        const test = qp.parseParams(params, options);\n        const expected = [\n          {\n            field: 'foo',\n            operator: '$eq',\n            value: 'cb1751fd-7fcf-4eb5-b38e-86428b1fd88d',\n          },\n          { field: 'bb', operator: '$eq', value: 1 },\n          { field: 'buz', operator: '$eq', value: 'string' },\n          { field: 'bigInt', operator: '$eq', value: '9007199254740999' },\n        ];\n        expect(test.paramsFilter).toMatchObject(expected);\n      });\n      it('should set paramsFilter with disabled validation', () => {\n        const params = {\n          foo: 'cb1751fd',\n          bar: '123',\n        };\n        const options: ParamsOptions = {\n          foo: { disabled: true },\n          bar: { field: 'bar', type: 'number' },\n        };\n        const test = qp.parseParams(params, options);\n        const expected = [{ field: 'bar', operator: '$eq', value: 123 }];\n        expect(test.paramsFilter).toMatchObject(expected);\n      });\n    });\n\n    describe('#setAuthPersist', () => {\n      it('it should set authPersist, 1', () => {\n        qp.setAuthPersist();\n        expect(qp.authPersist).toMatchObject({});\n      });\n      it('it should set authPersist, 2', () => {\n        const test = { foo: 'bar' };\n        qp.setAuthPersist(test);\n        const parsed = qp.getParsed();\n        expect(parsed.authPersist).toMatchObject(test);\n      });\n    });\n\n    describe('#setClassTransformOptions', () => {\n      it('it should set classTransformOptions, 1', () => {\n        qp.setClassTransformOptions();\n        expect(qp.classTransformOptions).toMatchObject({});\n      });\n      it('it should set classTransformOptions, 2', () => {\n        const testOptions = { groups: ['TEST'] };\n        qp.setClassTransformOptions(testOptions);\n        const parsed = qp.getParsed();\n        expect(parsed.classTransformOptions).toMatchObject(testOptions);\n      });\n    });\n\n    describe('#getParsed', () => {\n      it('should return parsed params', () => {\n        const expected: ParsedRequestParams = {\n          fields: [],\n          paramsFilter: [],\n          search: undefined,\n          authPersist: undefined,\n          classTransformOptions: undefined,\n          filter: [],\n          or: [],\n          join: [],\n          sort: [],\n          limit: undefined,\n          offset: undefined,\n          page: undefined,\n          cache: undefined,\n          includeDeleted: undefined,\n        };\n        const test = qp.getParsed();\n        expect(test).toMatchObject(expected);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud-request/test/request.query.validator.spec.ts",
    "content": "import { validateUUID } from '../src/request-query.validator';\n\ndescribe('#request-query', () => {\n  describe('#validator', () => {\n    describe('#validateUUID', () => {\n      const uuid = 'cf0917fc-af7d-11e9-a2a3-2a2ae2dbcce4';\n      const uuidV4 = '6650aad9-29bd-4601-b9b1-543a7a2d2d54';\n      const invalid = 'invalid-uuid';\n\n      it('should throw an error', () => {\n        expect(validateUUID.bind(validateUUID, invalid)).toThrow();\n      });\n      it('should pass, 1', () => {\n        expect(validateUUID(uuid, '')).toBeUndefined();\n      });\n      it('should pass, 2', () => {\n        expect(validateUUID(uuidV4, '')).toBeUndefined();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud-request/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"rootDir\": \"src\",\n    \"outDir\": \"lib\"\n  },\n  \"include\": [\"src\"],\n  \"exclude\": [\"lib\"],\n  \"references\": [{ \"path\": \"../util\" }]\n}\n"
  },
  {
    "path": "packages/crud-typeorm/README.md",
    "content": "<div align=\"center\">\n  <h1>CRUD (@nestjsx/crud-typeorm)</h1>\n</div>\n<div align=\"center\">\n  <strong>for RESTful APIs built with NestJs</strong>\n</div>\n\n<br />\n\n<div align=\"center\">\n  <a href=\"https://travis-ci.org/nestjsx/crud\">\n    <img src=\"https://github.com/nestjsx/crud/workflows/Tests/badge.svg\" alt=\"Build\" />\n  </a>\n  <a href=\"https://coveralls.io/github/nestjsx/crud?branch=master\">\n    <img src=\"https://coveralls.io/repos/github/nestjsx/crud/badge.svg\" alt=\"Coverage\" />\n  </a>\n  <a href=\"https://github.com/nestjsx/crud/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/nestjsx/crud.svg\" alt=\"License\" />\n  </a>\n  <a href=\"https://www.npmjs.com/package/@nestjsx/crud\">\n    <img src=\"https://img.shields.io/npm/v/@nestjsx/crud.svg\" alt=\"npm version\" />\n  </a>\n  <a href=\"https://www.npmjs.com/org/nestjsx\">\n    <img src=\"https://img.shields.io/npm/dm/@nestjsx/crud.svg\" alt=\"npm downloads\" />\n  </a>\n  <a href=\"https://npm.packagequality.com/#?package=@nestjsx%2Fcrud\">\n    <img src=\"https://npm.packagequality.com/shield/%40nestjsx%2Fcrud.svg\" alt=\"Package Quality\" />\n  </a>\n  <a href=\"https://renovatebot.com/\">\n    <img src=\"https://img.shields.io/badge/renovate-enabled-brightgreen.svg\" alt=\"Renovate\" />\n  </a>\n  <a href=\"http://makeapullrequest.com\">\n    <img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\" alt=\"PRs welcome\" />\n  </a>\n  <a href=\"https://github.com/marmelab/awesome-rest#nodejs\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-rest.svg?sanitize=true\" alt=\"Awesome REST\" />\n  </a>\n  <a href=\"https://github.com/juliandavidmr/awesome-nestjs#components--libraries\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-nest.svg?sanitize=true\" alt=\"Awesome Nest\" />\n  </a>\n  <a href=\"https://github.com/nestjs/nest\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/nest-powered.svg?sanitize=true\" alt=\"Nest Powered\" />\n  </a>\n  <a href=\"#individuals\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/backers/badge.svg\" />\n  </a>\n  <a href=\"#organizations\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/sponsors/badge.svg\" />\n  </a> \n</div>\n\n<div align=\"center\">\n  <sub>Built by\n  <a href=\"https://twitter.com/MichaelYali\">@MichaelYali</a> and\n  <a href=\"https://github.com/nestjsx/crud/graphs/contributors\">\n    Contributors\n  </a>\n</div>\n\n<br />\n\nWe believe that everyone who's working with NestJs and building some RESTful services and especially some CRUD functionality will find `@nestjsx/crud` microframework very useful.\n\n## Features\n\n<img align=\"right\" src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/crud-usage2.png\" alt=\"CRUD usage\" />\n\n- Super easy to install and start using the full-featured controllers and services :point_right:\n\n- DB and service agnostic extendable CRUD controllers\n\n- Reach query parsing with filtering, pagination, sorting, relations, nested relations, cache, etc.\n\n- Framework agnostic package with query builder for a frontend usage\n\n- Query, path params and DTOs validation included\n\n- Overriding controller methods with ease\n\n- Tiny config (including globally)\n\n- Additional helper decorators\n\n- Swagger documentation\n\n## Install\n\n```shell\nnpm i @nestjsx/crud-typeorm @nestjs/typeorm typeorm\n```\n\n## Packages\n\n- [**@nestjsx/crud**](https://www.npmjs.com/package/@nestjsx/crud) - core package which provides `@Crud()` decorator for endpoints generation, global configuration, validation, helper decorators ([docs](https://github.com/nestjsx/crud/wiki/Controllers#description))\n- [**@nestjsx/crud-request**](https://www.npmjs.com/package/@nestjsx/crud-request) - request builder/parser package which provides `RequestQueryBuilder` class for a frontend usage and `RequestQueryParser` that is being used internally for handling and validating query/path params on a backend side ([docs](https://github.com/nestjsx/crud/wiki/Requests#frontend-usage))\n- [**@nestjsx/crud-typeorm**](https://www.npmjs.com/package/@nestjsx/crud-typeorm) - TypeORM package which provides base `TypeOrmCrudService` with methods for CRUD database operations ([docs](https://github.com/nestjsx/crud/wiki/ServiceTypeorm))\n\n## Documentation\n\n- [General Information](https://github.com/nestjsx/crud/wiki#why)\n- [CRUD Controllers](https://github.com/nestjsx/crud/wiki/Controllers#description)\n- [CRUD ORM Services](https://github.com/nestjsx/crud/wiki/Services#description)\n- [Handling Requests](https://github.com/nestjsx/crud/wiki/Requests#description)\n\n## Support\n\nAny support is welcome. At least you can give us a star.\n\n## Contributors\n\n### Code Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].\n<a href=\"https://github.com/nestjsx/crud/graphs/contributors\"><img src=\"https://opencollective.com/nestjsx/contributors.svg?width=890&button=false\" /></a>\n\n### Financial Contributors\n\nBecome a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/nestjsx#backer)]\n\n#### Individuals\n\n<a href=\"https://opencollective.com/nestjsx#backers\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/backers.svg?width=890&button=false\"></a>\n\n#### Organizations\n\nSupport this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/nestjsx#sponsor)]\n\n<a href=\"https://opencollective.com/nestjsx/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/9/avatar.svg\"></a>\n\n## License\n\n[MIT](LICENSE)\n"
  },
  {
    "path": "packages/crud-typeorm/package.json",
    "content": "{\n  \"name\": \"@nestjsx/crud-typeorm\",\n  \"description\": \"NestJs CRUD for RESTful APIs - TypeORM\",\n  \"version\": \"5.0.0-alpha.3\",\n  \"license\": \"MIT\",\n  \"main\": \"lib/index.js\",\n  \"typings\": \"lib/index.d.ts\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"files\": [\n    \"lib\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/nestjsx/crud.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/nestjsx/crud/issues\"\n  },\n  \"keywords\": [\n    \"typescript\",\n    \"typeorm\",\n    \"nest\",\n    \"nestjs\",\n    \"rest\",\n    \"restful\",\n    \"api\",\n    \"crud\",\n    \"crud-generator\",\n    \"backend\",\n    \"frameworks\"\n  ],\n  \"author\": {\n    \"name\": \"Michael Yali\",\n    \"email\": \"mihon4ik@gmail.com\"\n  },\n  \"scripts\": {\n    \"build\": \"npx tsc -b\"\n  },\n  \"dependencies\": {\n    \"@zmotivat0r/o0\": \"^1.0.2\"\n  }\n}\n"
  },
  {
    "path": "packages/crud-typeorm/src/index.ts",
    "content": "export * from './typeorm-crud.service';\n"
  },
  {
    "path": "packages/crud-typeorm/src/typeorm-crud.service.ts",
    "content": "import {\n  CreateManyDto,\n  CrudRequest,\n  CrudRequestOptions,\n  CrudService,\n  GetManyDefaultResponse,\n  JoinOption,\n  JoinOptions,\n  QueryOptions,\n} from '@nestjsx/crud';\nimport {\n  ParsedRequestParams,\n  QueryFilter,\n  QueryJoin,\n  QuerySort,\n  SCondition,\n  SConditionKey,\n  ComparisonOperator,\n} from '@nestjsx/crud-request';\nimport { ClassType, hasLength, isArrayFull, isObject, isUndefined, objKeys, isNil, isNull } from '@nestjsx/util';\nimport { oO } from '@zmotivat0r/o0';\nimport { plainToClass } from 'class-transformer';\nimport {\n  Brackets,\n  DeepPartial,\n  ObjectLiteral,\n  Repository,\n  SelectQueryBuilder,\n  DataSourceOptions,\n  EntityMetadata,\n} from 'typeorm';\n\ninterface IAllowedRelation {\n  alias?: string;\n  nested: boolean;\n  name: string;\n  path: string;\n  columns: string[];\n  primaryColumns: string[];\n  allowedColumns: string[];\n}\n\nexport class TypeOrmCrudService<T> extends CrudService<T> {\n  protected dbName: DataSourceOptions['type'];\n\n  protected entityColumns: string[];\n\n  protected entityPrimaryColumns: string[];\n\n  protected entityHasDeleteColumn = false;\n\n  protected entityColumnsHash: ObjectLiteral = {};\n\n  protected entityRelationsHash: Map<string, IAllowedRelation> = new Map();\n\n  protected sqlInjectionRegEx: RegExp[] = [\n    /(%27)|(\\')|(--)|(%23)|(#)/gi,\n    /((%3D)|(=))[^\\n]*((%27)|(\\')|(--)|(%3B)|(;))/gi,\n    /w*((%27)|(\\'))((%6F)|o|(%4F))((%72)|r|(%52))/gi,\n    /((%27)|(\\'))union/gi,\n  ];\n\n  constructor(protected repo: Repository<T>) {\n    super();\n\n    this.dbName = this.repo.metadata.connection.options.type;\n    this.onInitMapEntityColumns();\n  }\n\n  public get findOne(): Repository<T>['findOne'] {\n    return this.repo.findOne.bind(this.repo);\n  }\n\n  public get find(): Repository<T>['find'] {\n    return this.repo.find.bind(this.repo);\n  }\n\n  public get count(): Repository<T>['count'] {\n    return this.repo.count.bind(this.repo);\n  }\n\n  protected get entityType(): ClassType<T> {\n    return this.repo.target as ClassType<T>;\n  }\n\n  protected get alias(): string {\n    return this.repo.metadata.targetName;\n  }\n\n  /**\n   * Get many\n   * @param req\n   */\n  public async getMany(req: CrudRequest): Promise<GetManyDefaultResponse<T> | T[]> {\n    const { parsed, options } = req;\n    const builder = await this.createBuilder(parsed, options);\n    return this.doGetMany(builder, parsed, options);\n  }\n\n  /**\n   * Get one\n   * @param req\n   */\n  public async getOne(req: CrudRequest): Promise<T> {\n    return this.getOneOrFail(req);\n  }\n\n  /**\n   * Create one\n   * @param req\n   * @param dto\n   */\n  public async createOne(req: CrudRequest, dto: T | Partial<T>): Promise<T> {\n    const { returnShallow } = req.options.routes.createOneBase;\n    const entity = this.prepareEntityBeforeSave(dto, req.parsed);\n\n    /* istanbul ignore if */\n    if (!entity) {\n      this.throwBadRequestException('Empty data. Nothing to save.');\n    }\n\n    const saved = await this.repo.save<any>(entity);\n\n    if (returnShallow) {\n      return saved;\n    } else {\n      const primaryParams = this.getPrimaryParams(req.options);\n\n      /* istanbul ignore next */\n      if (!primaryParams.length && primaryParams.some((p) => isNil(saved[p]))) {\n        return saved;\n      } else {\n        req.parsed.search = primaryParams.reduce((acc, p) => ({ ...acc, [p]: saved[p] }), {});\n        return this.getOneOrFail(req);\n      }\n    }\n  }\n\n  /**\n   * Create many\n   * @param req\n   * @param dto\n   */\n  public async createMany(req: CrudRequest, dto: CreateManyDto<T | Partial<T>>): Promise<T[]> {\n    /* istanbul ignore if */\n    if (!isObject(dto) || !isArrayFull(dto.bulk)) {\n      this.throwBadRequestException('Empty data. Nothing to save.');\n    }\n\n    const bulk = dto.bulk.map((one) => this.prepareEntityBeforeSave(one, req.parsed)).filter((d) => !isUndefined(d));\n\n    /* istanbul ignore if */\n    if (!hasLength(bulk)) {\n      this.throwBadRequestException('Empty data. Nothing to save.');\n    }\n\n    return this.repo.save<any>(bulk, { chunk: 50 });\n  }\n\n  /**\n   * Update one\n   * @param req\n   * @param dto\n   */\n  public async updateOne(req: CrudRequest, dto: T | Partial<T>): Promise<T> {\n    const { allowParamsOverride, returnShallow } = req.options.routes.updateOneBase;\n    const paramsFilters = this.getParamFilters(req.parsed);\n    const found = await this.getOneOrFail(req, returnShallow);\n    const toSave = !allowParamsOverride\n      ? { ...found, ...dto, ...paramsFilters, ...req.parsed.authPersist }\n      : { ...found, ...dto, ...req.parsed.authPersist };\n    const updated = await this.repo.save(\n      plainToClass(this.entityType, toSave, req.parsed.classTransformOptions) as unknown as DeepPartial<T>,\n    );\n\n    if (returnShallow) {\n      return updated;\n    } else {\n      req.parsed.paramsFilter.forEach((filter) => {\n        filter.value = updated[filter.field];\n      });\n\n      return this.getOneOrFail(req);\n    }\n  }\n\n  /**\n   * Recover one\n   * @param req\n   * @param dto\n   */\n  public async recoverOne(req: CrudRequest): Promise<T> {\n    const found = await this.getOneOrFail(req, false, true);\n    return this.repo.recover(found as unknown as DeepPartial<T>);\n  }\n\n  /**\n   * Replace one\n   * @param req\n   * @param dto\n   */\n  public async replaceOne(req: CrudRequest, dto: T | Partial<T>): Promise<T> {\n    const { allowParamsOverride, returnShallow } = req.options.routes.replaceOneBase;\n    const paramsFilters = this.getParamFilters(req.parsed);\n    const [_, found] = await oO(this.getOneOrFail(req, returnShallow));\n    const toSave = !allowParamsOverride\n      ? { ...(found || {}), ...dto, ...paramsFilters, ...req.parsed.authPersist }\n      : {\n          ...(found || /* istanbul ignore next */ {}),\n          ...paramsFilters,\n          ...dto,\n          ...req.parsed.authPersist,\n        };\n    const replaced = await this.repo.save(\n      plainToClass(this.entityType, toSave, req.parsed.classTransformOptions) as unknown as DeepPartial<T>,\n    );\n\n    if (returnShallow) {\n      return replaced;\n    } else {\n      const primaryParams = this.getPrimaryParams(req.options);\n\n      /* istanbul ignore if */\n      if (!primaryParams.length) {\n        return replaced;\n      }\n\n      req.parsed.search = primaryParams.reduce((acc, p) => ({ ...acc, [p]: replaced[p] }), {});\n      return this.getOneOrFail(req);\n    }\n  }\n\n  /**\n   * Delete one\n   * @param req\n   */\n  public async deleteOne(req: CrudRequest): Promise<void | T> {\n    const { returnDeleted } = req.options.routes.deleteOneBase;\n    const found = await this.getOneOrFail(req, returnDeleted);\n    const toReturn = returnDeleted\n      ? plainToClass(this.entityType, { ...found }, req.parsed.classTransformOptions)\n      : undefined;\n    const deleted =\n      req.options.query.softDelete === true\n        ? await this.repo.softRemove(found as unknown as DeepPartial<T>)\n        : await this.repo.remove(found);\n    return toReturn;\n  }\n\n  public getParamFilters(parsed: CrudRequest['parsed']): ObjectLiteral {\n    const filters = {};\n\n    /* istanbul ignore else */\n    if (hasLength(parsed.paramsFilter)) {\n      for (const filter of parsed.paramsFilter) {\n        filters[filter.field] = filter.value;\n      }\n    }\n\n    return filters;\n  }\n\n  /**\n   * Create TypeOrm QueryBuilder\n   * @param parsed\n   * @param options\n   * @param many\n   */\n  public async createBuilder(\n    parsed: ParsedRequestParams,\n    options: CrudRequestOptions,\n    many = true,\n    withDeleted = false,\n  ): Promise<SelectQueryBuilder<T>> {\n    // create query builder\n    const builder = this.repo.createQueryBuilder(this.alias);\n    // get select fields\n    const select = this.getSelect(parsed, options.query);\n    // select fields\n    builder.select(select);\n\n    // search\n    this.setSearchCondition(builder, parsed.search);\n\n    // set joins\n    const joinOptions = options.query.join || {};\n    const allowedJoins = objKeys(joinOptions);\n\n    if (hasLength(allowedJoins)) {\n      const eagerJoins: any = {};\n\n      for (let i = 0; i < allowedJoins.length; i++) {\n        /* istanbul ignore else */\n        if (joinOptions[allowedJoins[i]].eager) {\n          const cond = parsed.join.find((j) => j && j.field === allowedJoins[i]) || {\n            field: allowedJoins[i],\n          };\n          this.setJoin(cond, joinOptions, builder);\n          eagerJoins[allowedJoins[i]] = true;\n        }\n      }\n\n      if (isArrayFull(parsed.join)) {\n        for (let i = 0; i < parsed.join.length; i++) {\n          /* istanbul ignore else */\n          if (!eagerJoins[parsed.join[i].field]) {\n            this.setJoin(parsed.join[i], joinOptions, builder);\n          }\n        }\n      }\n    }\n\n    // if soft deleted is enabled add where statement to filter deleted records\n    if (this.entityHasDeleteColumn && options.query.softDelete) {\n      if (parsed.includeDeleted === 1 || withDeleted) {\n        builder.withDeleted();\n      }\n    }\n\n    /* istanbul ignore else */\n    if (many) {\n      // set sort (order by)\n      const sort = this.getSort(parsed, options.query);\n      builder.orderBy(sort);\n\n      // set take\n      const take = this.getTake(parsed, options.query);\n      /* istanbul ignore else */\n      if (isFinite(take)) {\n        builder.take(take);\n      }\n\n      // set skip\n      const skip = this.getSkip(parsed, take);\n      /* istanbul ignore else */\n      if (isFinite(skip)) {\n        builder.skip(skip);\n      }\n    }\n\n    // set cache\n    /* istanbul ignore else */\n    if (options.query.cache && parsed.cache !== 0) {\n      builder.cache(builder.getQueryAndParameters(), options.query.cache);\n    }\n\n    return builder;\n  }\n\n  /**\n   * depends on paging call `SelectQueryBuilder#getMany` or `SelectQueryBuilder#getManyAndCount`\n   * helpful for overriding `TypeOrmCrudService#getMany`\n   * @see getMany\n   * @see SelectQueryBuilder#getMany\n   * @see SelectQueryBuilder#getManyAndCount\n   * @param builder\n   * @param query\n   * @param options\n   */\n  protected async doGetMany(\n    builder: SelectQueryBuilder<T>,\n    query: ParsedRequestParams,\n    options: CrudRequestOptions,\n  ): Promise<GetManyDefaultResponse<T> | T[]> {\n    if (this.decidePagination(query, options)) {\n      const [data, total] = await builder.getManyAndCount();\n      const limit = builder.expressionMap.take;\n      const offset = builder.expressionMap.skip;\n\n      return this.createPageInfo(data, total, limit || total, offset || 0);\n    }\n\n    return builder.getMany();\n  }\n\n  protected onInitMapEntityColumns() {\n    this.entityColumns = this.repo.metadata.columns.map((prop) => {\n      // In case column is an embedded, use the propertyPath to get complete path\n      if (prop.embeddedMetadata) {\n        this.entityColumnsHash[prop.propertyPath] = prop.databasePath;\n        return prop.propertyPath;\n      }\n      this.entityColumnsHash[prop.propertyName] = prop.databasePath;\n      return prop.propertyName;\n    });\n    this.entityPrimaryColumns = this.repo.metadata.columns\n      .filter((prop) => prop.isPrimary)\n      .map((prop) => prop.propertyName);\n    this.entityHasDeleteColumn = this.repo.metadata.columns.filter((prop) => prop.isDeleteDate).length > 0;\n  }\n\n  protected async getOneOrFail(req: CrudRequest, shallow = false, withDeleted = false): Promise<T> {\n    const { parsed, options } = req;\n    const builder = shallow\n      ? this.repo.createQueryBuilder(this.alias)\n      : await this.createBuilder(parsed, options, true, withDeleted);\n\n    if (shallow) {\n      this.setSearchCondition(builder, parsed.search);\n    }\n\n    const found = withDeleted ? await builder.withDeleted().getOne() : await builder.getOne();\n\n    if (!found) {\n      this.throwNotFoundException(this.alias);\n    }\n\n    return found;\n  }\n\n  protected prepareEntityBeforeSave(dto: T | Partial<T>, parsed: CrudRequest['parsed']): T {\n    /* istanbul ignore if */\n    if (!isObject(dto)) {\n      return undefined;\n    }\n\n    if (hasLength(parsed.paramsFilter)) {\n      for (const filter of parsed.paramsFilter) {\n        dto[filter.field] = filter.value;\n      }\n    }\n\n    /* istanbul ignore if */\n    if (!hasLength(objKeys(dto))) {\n      return undefined;\n    }\n\n    return dto instanceof this.entityType\n      ? Object.assign(dto, parsed.authPersist)\n      : plainToClass(this.entityType, { ...dto, ...parsed.authPersist }, parsed.classTransformOptions);\n  }\n\n  protected getAllowedColumns(columns: string[], options: QueryOptions): string[] {\n    return (!options.exclude || !options.exclude.length) &&\n      (!options.allow || /* istanbul ignore next */ !options.allow.length)\n      ? columns\n      : columns.filter(\n          (column) =>\n            (options.exclude && options.exclude.length\n              ? !options.exclude.some((col) => col === column)\n              : /* istanbul ignore next */ true) &&\n            (options.allow && options.allow.length\n              ? options.allow.some((col) => col === column)\n              : /* istanbul ignore next */ true),\n        );\n  }\n\n  protected getEntityColumns(entityMetadata: EntityMetadata): { columns: string[]; primaryColumns: string[] } {\n    const columns = entityMetadata.columns.map((prop) => prop.propertyPath) || /* istanbul ignore next */ [];\n    const primaryColumns =\n      entityMetadata.primaryColumns.map((prop) => prop.propertyPath) || /* istanbul ignore next */ [];\n\n    return { columns, primaryColumns };\n  }\n\n  protected getRelationMetadata(field: string, options: JoinOption): IAllowedRelation {\n    try {\n      let allowedRelation;\n      let nested = false;\n\n      if (this.entityRelationsHash.has(field)) {\n        allowedRelation = this.entityRelationsHash.get(field);\n      } else {\n        const fields = field.split('.');\n        let relationMetadata: EntityMetadata;\n        let name: string;\n        let path: string;\n        let parentPath: string;\n\n        if (fields.length === 1) {\n          const found = this.repo.metadata.relations.find((one) => one.propertyName === fields[0]);\n\n          if (found) {\n            name = fields[0];\n            path = `${this.alias}.${fields[0]}`;\n            relationMetadata = found.inverseEntityMetadata;\n          }\n        } else {\n          nested = true;\n          parentPath = '';\n\n          const reduced = fields.reduce(\n            (res, propertyName: string, i) => {\n              const found = res.relations.length\n                ? res.relations.find((one) => one.propertyName === propertyName)\n                : null;\n              const relationMetadata = found ? found.inverseEntityMetadata : null;\n              const relations = relationMetadata ? relationMetadata.relations : [];\n              name = propertyName;\n\n              if (i !== fields.length - 1) {\n                parentPath = !parentPath ? propertyName : /* istanbul ignore next */ `${parentPath}.${propertyName}`;\n              }\n\n              return {\n                relations,\n                relationMetadata,\n              };\n            },\n            {\n              relations: this.repo.metadata.relations,\n              relationMetadata: null,\n            },\n          );\n\n          relationMetadata = reduced.relationMetadata;\n        }\n\n        if (relationMetadata) {\n          const { columns, primaryColumns } = this.getEntityColumns(relationMetadata);\n\n          if (!path && parentPath) {\n            const parentAllowedRelation = this.entityRelationsHash.get(parentPath);\n\n            /* istanbul ignore next */\n            if (parentAllowedRelation) {\n              path = parentAllowedRelation.alias ? `${parentAllowedRelation.alias}.${name}` : field;\n            }\n          }\n\n          allowedRelation = {\n            alias: options.alias,\n            name,\n            path,\n            columns,\n            nested,\n            primaryColumns,\n          };\n        }\n      }\n\n      if (allowedRelation) {\n        const allowedColumns = this.getAllowedColumns(allowedRelation.columns, options);\n        const toSave: IAllowedRelation = { ...allowedRelation, allowedColumns };\n\n        this.entityRelationsHash.set(field, toSave);\n\n        if (options.alias) {\n          this.entityRelationsHash.set(options.alias, toSave);\n        }\n\n        return toSave;\n      }\n    } catch (_) {\n      /* istanbul ignore next */\n      return null;\n    }\n  }\n\n  protected setJoin(cond: QueryJoin, joinOptions: JoinOptions, builder: SelectQueryBuilder<T>) {\n    const options = joinOptions[cond.field];\n\n    if (!options) {\n      return true;\n    }\n\n    const allowedRelation = this.getRelationMetadata(cond.field, options);\n\n    if (!allowedRelation) {\n      return true;\n    }\n\n    const relationType = options.required ? 'innerJoin' : 'leftJoin';\n    const alias = options.alias ? options.alias : allowedRelation.name;\n\n    builder[relationType](allowedRelation.path, alias);\n\n    if (options.select !== false) {\n      const columns = isArrayFull(cond.select)\n        ? cond.select.filter((column) => allowedRelation.allowedColumns.some((allowed) => allowed === column))\n        : allowedRelation.allowedColumns;\n\n      const select = new Set(\n        [...allowedRelation.primaryColumns, ...(isArrayFull(options.persist) ? options.persist : []), ...columns].map(\n          (col) => `${alias}.${col}`,\n        ),\n      );\n\n      builder.addSelect(Array.from(select));\n    }\n  }\n\n  protected setAndWhere(cond: QueryFilter, i: any, builder: SelectQueryBuilder<T>) {\n    const { str, params } = this.mapOperatorsToQuery(cond, `andWhere${i}`);\n    builder.andWhere(str, params);\n  }\n\n  protected setOrWhere(cond: QueryFilter, i: any, builder: SelectQueryBuilder<T>) {\n    const { str, params } = this.mapOperatorsToQuery(cond, `orWhere${i}`);\n    builder.orWhere(str, params);\n  }\n\n  protected setSearchCondition(builder: SelectQueryBuilder<T>, search: SCondition, condition: SConditionKey = '$and') {\n    /* istanbul ignore else */\n    if (isObject(search)) {\n      const keys = objKeys(search);\n      /* istanbul ignore else */\n      if (keys.length) {\n        // search: {$and: [...], ...}\n        if (isArrayFull(search.$and)) {\n          // search: {$and: [{}]}\n          if (search.$and.length === 1) {\n            this.setSearchCondition(builder, search.$and[0], condition);\n          }\n          // search: {$and: [{}, {}, ...]}\n          else {\n            this.builderAddBrackets(\n              builder,\n              condition,\n              new Brackets((qb: any) => {\n                search.$and.forEach((item: any) => {\n                  this.setSearchCondition(qb, item, '$and');\n                });\n              }),\n            );\n          }\n        }\n        // search: {$or: [...], ...}\n        else if (isArrayFull(search.$or)) {\n          // search: {$or: [...]}\n          if (keys.length === 1) {\n            // search: {$or: [{}]}\n            if (search.$or.length === 1) {\n              this.setSearchCondition(builder, search.$or[0], condition);\n            }\n            // search: {$or: [{}, {}, ...]}\n            else {\n              this.builderAddBrackets(\n                builder,\n                condition,\n                new Brackets((qb: any) => {\n                  search.$or.forEach((item: any) => {\n                    this.setSearchCondition(qb, item, '$or');\n                  });\n                }),\n              );\n            }\n          }\n          // search: {$or: [...], foo, ...}\n          else {\n            this.builderAddBrackets(\n              builder,\n              condition,\n              new Brackets((qb: any) => {\n                keys.forEach((field: string) => {\n                  if (field !== '$or') {\n                    const value = search[field];\n                    if (!isObject(value)) {\n                      this.builderSetWhere(qb, '$and', field, value);\n                    } else {\n                      this.setSearchFieldObjectCondition(qb, '$and', field, value);\n                    }\n                  } else {\n                    if (search.$or.length === 1) {\n                      this.setSearchCondition(builder, search.$or[0], '$and');\n                    } else {\n                      this.builderAddBrackets(\n                        qb,\n                        '$and',\n                        new Brackets((qb2: any) => {\n                          search.$or.forEach((item: any) => {\n                            this.setSearchCondition(qb2, item, '$or');\n                          });\n                        }),\n                      );\n                    }\n                  }\n                });\n              }),\n            );\n          }\n        }\n        // search: {...}\n        else {\n          // search: {foo}\n          if (keys.length === 1) {\n            const field = keys[0];\n            const value = search[field];\n            if (!isObject(value)) {\n              this.builderSetWhere(builder, condition, field, value);\n            } else {\n              this.setSearchFieldObjectCondition(builder, condition, field, value);\n            }\n          }\n          // search: {foo, ...}\n          else {\n            this.builderAddBrackets(\n              builder,\n              condition,\n              new Brackets((qb: any) => {\n                keys.forEach((field: string) => {\n                  const value = search[field];\n                  if (!isObject(value)) {\n                    this.builderSetWhere(qb, '$and', field, value);\n                  } else {\n                    this.setSearchFieldObjectCondition(qb, '$and', field, value);\n                  }\n                });\n              }),\n            );\n          }\n        }\n      }\n    }\n  }\n\n  protected builderAddBrackets(builder: SelectQueryBuilder<T>, condition: SConditionKey, brackets: Brackets) {\n    if (condition === '$and') {\n      builder.andWhere(brackets);\n    } else {\n      builder.orWhere(brackets);\n    }\n  }\n\n  protected builderSetWhere(\n    builder: SelectQueryBuilder<T>,\n    condition: SConditionKey,\n    field: string,\n    value: any,\n    operator: ComparisonOperator = '$eq',\n  ) {\n    const time = process.hrtime();\n    const index = `${field}${time[0]}${time[1]}`;\n    const args = [{ field, operator: isNull(value) ? '$isnull' : operator, value }, index, builder];\n    const fn = condition === '$and' ? this.setAndWhere : this.setOrWhere;\n    fn.apply(this, args);\n  }\n\n  protected setSearchFieldObjectCondition(\n    builder: SelectQueryBuilder<T>,\n    condition: SConditionKey,\n    field: string,\n    object: any,\n  ) {\n    /* istanbul ignore else */\n    if (isObject(object)) {\n      const operators = objKeys(object);\n\n      if (operators.length === 1) {\n        const operator = operators[0] as ComparisonOperator;\n        const value = object[operator];\n\n        if (isObject(object.$or)) {\n          const orKeys = objKeys(object.$or);\n          this.setSearchFieldObjectCondition(builder, orKeys.length === 1 ? condition : '$or', field, object.$or);\n        } else {\n          this.builderSetWhere(builder, condition, field, value, operator);\n        }\n      } else {\n        /* istanbul ignore else */\n        if (operators.length > 1) {\n          this.builderAddBrackets(\n            builder,\n            condition,\n            new Brackets((qb: any) => {\n              operators.forEach((operator: ComparisonOperator) => {\n                const value = object[operator];\n\n                if (operator !== '$or') {\n                  this.builderSetWhere(qb, condition, field, value, operator);\n                } else {\n                  const orKeys = objKeys(object.$or);\n\n                  if (orKeys.length === 1) {\n                    this.setSearchFieldObjectCondition(qb, condition, field, object.$or);\n                  } else {\n                    this.builderAddBrackets(\n                      qb,\n                      condition,\n                      new Brackets((qb2: any) => {\n                        this.setSearchFieldObjectCondition(qb2, '$or', field, object.$or);\n                      }),\n                    );\n                  }\n                }\n              });\n            }),\n          );\n        }\n      }\n    }\n  }\n\n  protected getSelect(query: ParsedRequestParams, options: QueryOptions): string[] {\n    const allowed = this.getAllowedColumns(this.entityColumns, options);\n\n    const columns =\n      query.fields && query.fields.length\n        ? query.fields.filter((field) => allowed.some((col) => field === col))\n        : allowed;\n\n    const select = new Set(\n      [\n        ...(options.persist && options.persist.length ? options.persist : []),\n        ...columns,\n        ...this.entityPrimaryColumns,\n      ].map((col) => `${this.alias}.${col}`),\n    );\n\n    return Array.from(select);\n  }\n\n  protected getSort(query: ParsedRequestParams, options: QueryOptions) {\n    return query.sort && query.sort.length\n      ? this.mapSort(query.sort)\n      : options.sort && options.sort.length\n      ? this.mapSort(options.sort)\n      : {};\n  }\n\n  protected getFieldWithAlias(field: string, sort = false) {\n    /* istanbul ignore next */\n    const i = ['mysql','mariadb'].includes(this.dbName) ? '`' : '\"';\n    const cols = field.split('.');\n\n    switch (cols.length) {\n      case 1:\n        if (sort) {\n          return `${this.alias}.${field}`;\n        }\n\n        const dbColName = this.entityColumnsHash[field] !== field ? this.entityColumnsHash[field] : field;\n\n        return `${i}${this.alias}${i}.${i}${dbColName}${i}`;\n      case 2:\n        return field;\n      default:\n        return cols.slice(cols.length - 2, cols.length).join('.');\n    }\n  }\n\n  protected mapSort(sort: QuerySort[]) {\n    const params: ObjectLiteral = {};\n\n    for (let i = 0; i < sort.length; i++) {\n      const field = this.getFieldWithAlias(sort[i].field, true);\n      const checkedFiled = this.checkSqlInjection(field);\n      params[checkedFiled] = sort[i].order;\n    }\n\n    return params;\n  }\n\n  protected mapOperatorsToQuery(cond: QueryFilter, param: any): { str: string; params: ObjectLiteral } {\n    const field = this.getFieldWithAlias(cond.field);\n    const likeOperator = this.dbName === 'postgres' ? 'ILIKE' : /* istanbul ignore next */ 'LIKE';\n    let str: string;\n    let params: ObjectLiteral;\n\n    if (cond.operator[0] !== '$') {\n      cond.operator = ('$' + cond.operator) as ComparisonOperator;\n    }\n\n    switch (cond.operator) {\n      case '$eq':\n        str = `${field} = :${param}`;\n        break;\n\n      case '$ne':\n        str = `${field} != :${param}`;\n        break;\n\n      case '$gt':\n        str = `${field} > :${param}`;\n        break;\n\n      case '$lt':\n        str = `${field} < :${param}`;\n        break;\n\n      case '$gte':\n        str = `${field} >= :${param}`;\n        break;\n\n      case '$lte':\n        str = `${field} <= :${param}`;\n        break;\n\n      case '$starts':\n        str = `${field} LIKE :${param}`;\n        params = { [param]: `${cond.value}%` };\n        break;\n\n      case '$ends':\n        str = `${field} LIKE :${param}`;\n        params = { [param]: `%${cond.value}` };\n        break;\n\n      case '$cont':\n        str = `${field} LIKE :${param}`;\n        params = { [param]: `%${cond.value}%` };\n        break;\n\n      case '$excl':\n        str = `${field} NOT LIKE :${param}`;\n        params = { [param]: `%${cond.value}%` };\n        break;\n\n      case '$in':\n        this.checkFilterIsArray(cond);\n        str = `${field} IN (:...${param})`;\n        break;\n\n      case '$notin':\n        this.checkFilterIsArray(cond);\n        str = `${field} NOT IN (:...${param})`;\n        break;\n\n      case '$isnull':\n        str = `${field} IS NULL`;\n        params = {};\n        break;\n\n      case '$notnull':\n        str = `${field} IS NOT NULL`;\n        params = {};\n        break;\n\n      case '$between':\n        this.checkFilterIsArray(cond, cond.value.length !== 2);\n        str = `${field} BETWEEN :${param}0 AND :${param}1`;\n        params = {\n          [`${param}0`]: cond.value[0],\n          [`${param}1`]: cond.value[1],\n        };\n        break;\n\n      // case insensitive\n      case '$eqL':\n        str = `LOWER(${field}) = :${param}`;\n        break;\n\n      case '$neL':\n        str = `LOWER(${field}) != :${param}`;\n        break;\n\n      case '$startsL':\n        str = `LOWER(${field}) ${likeOperator} :${param}`;\n        params = { [param]: `${cond.value}%` };\n        break;\n\n      case '$endsL':\n        str = `LOWER(${field}) ${likeOperator} :${param}`;\n        params = { [param]: `%${cond.value}` };\n        break;\n\n      case '$contL':\n        str = `LOWER(${field}) ${likeOperator} :${param}`;\n        params = { [param]: `%${cond.value}%` };\n        break;\n\n      case '$exclL':\n        str = `LOWER(${field}) NOT ${likeOperator} :${param}`;\n        params = { [param]: `%${cond.value}%` };\n        break;\n\n      case '$inL':\n        this.checkFilterIsArray(cond);\n        str = `LOWER(${field}) IN (:...${param})`;\n        break;\n\n      case '$notinL':\n        this.checkFilterIsArray(cond);\n        str = `LOWER(${field}) NOT IN (:...${param})`;\n        break;\n\n      /* istanbul ignore next */\n      default:\n        str = `${field} = :${param}`;\n        break;\n    }\n\n    if (typeof params === 'undefined') {\n      params = { [param]: cond.value };\n    }\n\n    return { str, params };\n  }\n\n  private checkFilterIsArray(cond: QueryFilter, withLength?: boolean) {\n    /* istanbul ignore if */\n    if (!Array.isArray(cond.value) || !cond.value.length || (!isNil(withLength) ? withLength : false)) {\n      this.throwBadRequestException(`Invalid column '${cond.field}' value`);\n    }\n  }\n\n  private checkSqlInjection(field: string): string {\n    /* istanbul ignore else */\n    if (this.sqlInjectionRegEx.length) {\n      for (let i = 0; i < this.sqlInjectionRegEx.length; i++) {\n        /* istanbul ignore else */\n        if (this.sqlInjectionRegEx[0].test(field)) {\n          this.throwBadRequestException(`SQL injection detected: \"${field}\"`);\n        }\n      }\n    }\n\n    return field;\n  }\n}\n"
  },
  {
    "path": "packages/crud-typeorm/test/__fixture__/companies.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\n\nimport { TypeOrmCrudService } from '../../../crud-typeorm/src/typeorm-crud.service';\nimport { Company } from '../../../../integration/crud-typeorm/companies';\n\n@Injectable()\nexport class CompaniesService extends TypeOrmCrudService<Company> {\n  constructor(@InjectRepository(Company) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "packages/crud-typeorm/test/__fixture__/devices.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\n\nimport { TypeOrmCrudService } from '../../../crud-typeorm/src/typeorm-crud.service';\nimport { Device } from '../../../../integration/crud-typeorm/devices';\n\n@Injectable()\nexport class DevicesService extends TypeOrmCrudService<Device> {\n  constructor(@InjectRepository(Device) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "packages/crud-typeorm/test/__fixture__/notes.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\n\nimport { TypeOrmCrudService } from '../../../crud-typeorm/src/typeorm-crud.service';\nimport { Note } from '../../../../integration/crud-typeorm/notes';\n\n@Injectable()\nexport class NotesService extends TypeOrmCrudService<Note> {\n  constructor(@InjectRepository(Note) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "packages/crud-typeorm/test/__fixture__/projects.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\n\nimport { TypeOrmCrudService } from '../../../crud-typeorm/src/typeorm-crud.service';\nimport { Project } from '../../../../integration/crud-typeorm/projects';\n\n@Injectable()\nexport class ProjectsService extends TypeOrmCrudService<Project> {\n  constructor(@InjectRepository(Project) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "packages/crud-typeorm/test/__fixture__/users.service.ts",
    "content": "import { Injectable } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\n\nimport { TypeOrmCrudService } from '../../../crud-typeorm/src/typeorm-crud.service';\nimport { User } from '../../../../integration/crud-typeorm/users';\n\n@Injectable()\nexport class UsersService extends TypeOrmCrudService<User> {\n  constructor(@InjectRepository(User) repo) {\n    super(repo);\n  }\n}\n\n@Injectable()\nexport class UsersService2 extends TypeOrmCrudService<User> {\n  constructor(@InjectRepository(User) repo) {\n    super(repo);\n  }\n}\n"
  },
  {
    "path": "packages/crud-typeorm/test/a.params-options.spec.ts",
    "content": "import 'jest-extended';\nimport { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\nimport { Test } from '@nestjs/testing';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport * as request from 'supertest';\n\nimport { Company } from '../../../integration/crud-typeorm/companies';\nimport { withCache } from '../../../integration/crud-typeorm/orm.config';\nimport { Project } from '../../../integration/crud-typeorm/projects';\nimport { User } from '../../../integration/crud-typeorm/users';\nimport { UserProfile } from '../../../integration/crud-typeorm/users-profiles';\nimport { HttpExceptionFilter } from '../../../integration/shared/https-exception.filter';\nimport { Crud } from '../../crud/src/decorators/crud.decorator';\nimport { UsersService } from './__fixture__/users.service';\n\n// tslint:disable:max-classes-per-file\ndescribe('#crud-typeorm', () => {\n  describe('#params options', () => {\n    let app: INestApplication;\n    let server: any;\n\n    @Crud({\n      model: { type: User },\n      params: {\n        companyId: {\n          field: 'companyId',\n          type: 'number',\n        },\n        id: {\n          field: 'id',\n          type: 'number',\n          primary: true,\n        },\n      },\n      routes: {\n        updateOneBase: {\n          allowParamsOverride: true,\n          returnShallow: true,\n        },\n        replaceOneBase: {\n          allowParamsOverride: true,\n          returnShallow: true,\n        },\n      },\n    })\n    @Controller('/companiesA/:companyId/users')\n    class UsersController1 {\n      constructor(public service: UsersService) {}\n    }\n\n    @Crud({\n      model: { type: User },\n      params: {\n        companyId: {\n          field: 'companyId',\n          type: 'number',\n        },\n        id: {\n          field: 'id',\n          type: 'number',\n          primary: true,\n        },\n      },\n      query: {\n        join: {\n          company: {\n            eager: true,\n          },\n        },\n      },\n    })\n    @Controller('/companiesB/:companyId/users')\n    class UsersController2 {\n      constructor(public service: UsersService) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        imports: [\n          TypeOrmModule.forRoot({ ...withCache, logging: false }),\n          TypeOrmModule.forFeature([Company, Project, User, UserProfile]),\n        ],\n        controllers: [UsersController1, UsersController2],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, UsersService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    afterAll(async () => {\n      await app.close();\n    });\n\n    describe('#updateOneBase', () => {\n      it('should override params', async () => {\n        const dto = { isActive: false, companyId: 2 };\n        const res = await request(server)\n          .patch('/companiesA/1/users/2')\n          .send(dto)\n          .expect(200);\n        expect(res.body.companyId).toBe(2);\n      });\n      it('should not override params', async () => {\n        const dto = { isActive: false, companyId: 2 };\n        const res = await request(server)\n          .patch('/companiesB/1/users/3')\n          .send(dto)\n          .expect(200);\n        expect(res.body.companyId).toBe(1);\n      });\n      it('should return full entity', async () => {\n        const dto = { isActive: false };\n        const res = await request(server)\n          .patch('/companiesB/2/users/2')\n          .send(dto)\n          .expect(200);\n        expect(res.body.company.id).toBe(2);\n      });\n      it('should return shallow entity', async () => {\n        const dto = { isActive: false };\n        const res = await request(server)\n          .patch('/companiesA/2/users/2')\n          .send(dto)\n          .expect(200);\n        expect(res.body.company).toBeUndefined();\n      });\n    });\n\n    describe('#replaceOneBase', () => {\n      it('should override params', async () => {\n        const dto = { isActive: false, companyId: 2, email: '4@email.com' };\n        const res = await request(server)\n          .put('/companiesA/1/users/4')\n          .send(dto)\n          .expect(200);\n        expect(res.body.companyId).toBe(2);\n      });\n      it('should not override params', async () => {\n        const dto = { isActive: false, companyId: 1 };\n        const res = await request(server)\n          .put('/companiesB/2/users/4')\n          .send(dto)\n          .expect(200);\n        expect(res.body.companyId).toBe(2);\n      });\n      it('should return full entity', async () => {\n        const dto = { isActive: false };\n        const res = await request(server)\n          .put('/companiesB/2/users/4')\n          .send(dto)\n          .expect(200);\n        expect(res.body.company.id).toBe(2);\n      });\n      it('should return shallow entity', async () => {\n        const dto = { isActive: false };\n        const res = await request(server)\n          .put('/companiesA/2/users/4')\n          .send(dto)\n          .expect(200);\n        expect(res.body.company).toBeUndefined();\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud-typeorm/test/b.query-params.spec.ts",
    "content": "import { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\nimport { Test } from '@nestjs/testing';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport { RequestQueryBuilder } from '@nestjsx/crud-request';\nimport 'jest-extended';\nimport * as request from 'supertest';\n\nimport { Company } from '../../../integration/crud-typeorm/companies';\nimport { withCache } from '../../../integration/crud-typeorm/orm.config';\nimport { Project } from '../../../integration/crud-typeorm/projects';\nimport { User } from '../../../integration/crud-typeorm/users';\nimport { UserProfile } from '../../../integration/crud-typeorm/users-profiles';\nimport { Note } from '../../../integration/crud-typeorm/notes';\nimport { HttpExceptionFilter } from '../../../integration/shared/https-exception.filter';\nimport { Crud } from '../../crud/src/decorators';\nimport { CompaniesService } from './__fixture__/companies.service';\nimport { ProjectsService } from './__fixture__/projects.service';\nimport { UsersService, UsersService2 } from './__fixture__/users.service';\nimport { NotesService } from './__fixture__/notes.service';\n\n// tslint:disable:max-classes-per-file\ndescribe('#crud-typeorm', () => {\n  describe('#query params', () => {\n    let app: INestApplication;\n    let server: any;\n    let qb: RequestQueryBuilder;\n\n    @Crud({\n      model: { type: Company },\n      query: {\n        exclude: ['updatedAt'],\n        allow: ['id', 'name', 'domain', 'description'],\n        filter: [{ field: 'id', operator: 'ne', value: 1 }],\n        join: {\n          users: {\n            allow: ['id'],\n          },\n        },\n        maxLimit: 5,\n      },\n    })\n    @Controller('companies')\n    class CompaniesController {\n      constructor(public service: CompaniesService) {}\n    }\n\n    @Crud({\n      model: { type: Project },\n      routes: {\n        updateOneBase: {\n          returnShallow: true,\n        },\n      },\n      query: {\n        join: {\n          company: {\n            eager: true,\n            persist: ['id'],\n            exclude: ['updatedAt', 'createdAt'],\n          },\n          users: {},\n          userProjects: {},\n        },\n        sort: [{ field: 'id', order: 'ASC' }],\n        limit: 100,\n      },\n    })\n    @Controller('projects')\n    class ProjectsController {\n      constructor(public service: ProjectsService) {}\n    }\n\n    @Crud({\n      model: { type: Project },\n    })\n    @Controller('projects2')\n    class ProjectsController2 {\n      constructor(public service: ProjectsService) {}\n    }\n\n    @Crud({\n      model: { type: Project },\n      query: {\n        filter: [{ field: 'isActive', operator: 'eq', value: false }],\n      },\n    })\n    @Controller('projects3')\n    class ProjectsController3 {\n      constructor(public service: ProjectsService) {}\n    }\n\n    @Crud({\n      model: { type: Project },\n      query: {\n        filter: { isActive: true },\n      },\n    })\n    @Controller('projects4')\n    class ProjectsController4 {\n      constructor(public service: ProjectsService) {}\n    }\n\n    @Crud({\n      model: { type: User },\n      query: {\n        join: {\n          company: {},\n          'company.projects': {},\n          userLicenses: {},\n          invalid: {\n            eager: true,\n          },\n          'foo.bar': {\n            eager: true,\n          },\n        },\n      },\n    })\n    @Controller('users')\n    class UsersController {\n      constructor(public service: UsersService) {}\n    }\n\n    @Crud({\n      model: { type: User },\n      query: {\n        join: {\n          company: {},\n          'company.projects': {\n            alias: 'pr',\n          },\n        },\n      },\n    })\n    @Controller('users2')\n    class UsersController2 {\n      constructor(public service: UsersService) {}\n    }\n\n    @Crud({\n      model: { type: User },\n      query: {\n        join: {\n          company: {\n            alias: 'userCompany',\n            eager: true,\n            select: false,\n          },\n        },\n      },\n    })\n    @Controller('myusers')\n    class UsersController3 {\n      constructor(public service: UsersService2) {}\n    }\n\n    @Crud({\n      model: { type: Note },\n    })\n    @Controller('notes')\n    class NotesController {\n      constructor(public service: NotesService) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        imports: [\n          TypeOrmModule.forRoot({ ...withCache, logging: false }),\n          TypeOrmModule.forFeature([Company, Project, User, UserProfile, Note]),\n        ],\n        controllers: [\n          CompaniesController,\n          ProjectsController,\n          ProjectsController2,\n          ProjectsController3,\n          ProjectsController4,\n          UsersController,\n          UsersController2,\n          UsersController3,\n          NotesController,\n        ],\n        providers: [\n          { provide: APP_FILTER, useClass: HttpExceptionFilter },\n          CompaniesService,\n          UsersService,\n          UsersService2,\n          ProjectsService,\n          NotesService,\n        ],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    beforeEach(() => {\n      qb = RequestQueryBuilder.create();\n    });\n\n    afterAll(async () => {\n      await app.close();\n    });\n\n    describe('#select', () => {\n      it('should throw status 400', (done) => {\n        const query = qb.setFilter({ field: 'invalid', operator: 'isnull' }).query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(500);\n            done();\n          });\n      });\n    });\n\n    describe('#query filter', () => {\n      it('should return data with limit', (done) => {\n        const query = qb.setLimit(4).query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(4);\n            res.body.forEach((e: Company) => {\n              expect(e.id).not.toBe(1);\n            });\n            done();\n          });\n      });\n      it('should return with maxLimit', (done) => {\n        const query = qb.setLimit(7).query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(5);\n            done();\n          });\n      });\n      it('should return with filter and or, 1', (done) => {\n        const query = qb\n          .setFilter({ field: 'name', operator: 'notin', value: ['Name2', 'Name3'] })\n          .setOr({ field: 'domain', operator: 'cont', value: 5 })\n          .query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(5);\n            done();\n          });\n      });\n      it('should return with filter and or, 2', (done) => {\n        const query = qb\n          .setFilter({ field: 'name', operator: 'ends', value: 'foo' })\n          .setOr({ field: 'name', operator: 'starts', value: 'P' })\n          .setOr({ field: 'isActive', operator: 'eq', value: true })\n          .query();\n        request(server)\n          .get('/projects')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(10);\n            done();\n          });\n      });\n      it('should return with filter and or, 3', (done) => {\n        const query = qb\n          .setOr({ field: 'companyId', operator: 'gt', value: 22 })\n          .setFilter({ field: 'companyId', operator: 'gte', value: 6 })\n          .setFilter({ field: 'companyId', operator: 'lt', value: 10 })\n          .query();\n        request(server)\n          .get('/projects')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(8);\n            done();\n          });\n      });\n      it('should return with filter and or, 4', (done) => {\n        const query = qb\n          .setOr({ field: 'companyId', operator: 'in', value: [6, 10] })\n          .setOr({ field: 'companyId', operator: 'lte', value: 10 })\n          .setFilter({ field: 'isActive', operator: 'eq', value: false })\n          .setFilter({ field: 'description', operator: 'notnull' })\n          .query();\n        request(server)\n          .get('/projects')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(10);\n            done();\n          });\n      });\n      it('should return with filter and or, 6', (done) => {\n        const query = qb.setOr({ field: 'companyId', operator: 'isnull' }).query();\n        request(server)\n          .get('/projects')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(0);\n            done();\n          });\n      });\n      it('should return with filter and or, 6', (done) => {\n        const query = qb.setOr({ field: 'companyId', operator: 'between', value: [1, 5] }).query();\n        request(server)\n          .get('/projects')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(10);\n            done();\n          });\n      });\n      it('should return with filter, 1', (done) => {\n        const query = qb.setOr({ field: 'companyId', operator: 'eq', value: 1 }).query();\n        request(server)\n          .get('/projects')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(2);\n            done();\n          });\n      });\n    });\n\n    describe('#query join', () => {\n      it('should return joined entity, 1', (done) => {\n        const query = qb.setJoin({ field: 'company', select: ['name'] }).query();\n        request(server)\n          .get('/projects/2')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.company).toBeDefined();\n            done();\n          });\n      });\n      it('should return joined entity, 2', (done) => {\n        const query = qb.setJoin({ field: 'users', select: ['name'] }).query();\n        request(server)\n          .get('/companies/2')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.users).toBeDefined();\n            expect(res.body.users.length).not.toBe(0);\n            done();\n          });\n      });\n      it('should eager join without selection', (done) => {\n        const query = qb.search({ 'userCompany.id': { $eq: 1 } }).query();\n        request(server)\n          .get('/myusers')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(10);\n            expect(res.body[0].company).toBeUndefined();\n            done();\n          });\n      });\n    });\n\n    describe('#query nested join', () => {\n      it('should return status 400, 1', (done) => {\n        const query = qb\n          .setJoin({ field: 'company' })\n          .setJoin({ field: 'company.projects' })\n          .setFilter({\n            field: 'company.projects.foo',\n            operator: 'excl',\n            value: 'invalid',\n          })\n          .query();\n        request(server)\n          .get('/users/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(500);\n            done();\n          });\n      });\n      it('should return status 400, 2', (done) => {\n        const query = qb\n          .setJoin({ field: 'company' })\n          .setJoin({ field: 'company.projects' })\n          .setFilter({\n            field: 'invalid.projects',\n            operator: 'excl',\n            value: 'invalid',\n          })\n          .query();\n        request(server)\n          .get('/users/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(500);\n            done();\n          });\n      });\n      it('should return status 400, 3', (done) => {\n        const query = qb\n          .setJoin({ field: 'company' })\n          .setJoin({ field: 'company.projects' })\n          .setFilter({\n            field: 'company.foo',\n            operator: 'excl',\n            value: 'invalid',\n          })\n          .query();\n        request(server)\n          .get('/users/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(500);\n            done();\n          });\n      });\n      it('should return status 200', (done) => {\n        const query = qb.setJoin({ field: 'company' }).setJoin({ field: 'company.projectsinvalid' }).query();\n        request(server)\n          .get('/users/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            done();\n          });\n      });\n      it('should return joined entity, 1', (done) => {\n        const query = qb\n          .setFilter({ field: 'company.name', operator: 'excl', value: 'invalid' })\n          .setJoin({ field: 'company' })\n          .setJoin({ field: 'company.projects' })\n          .query();\n        request(server)\n          .get('/users/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.company).toBeDefined();\n            expect(res.body.company.projects).toBeDefined();\n            done();\n          });\n      });\n      it('should return joined entity, 2', (done) => {\n        const query = qb\n          .setFilter({ field: 'company.projects.id', operator: 'notnull' })\n          .setJoin({ field: 'company' })\n          .setJoin({ field: 'company.projects' })\n          .query();\n        request(server)\n          .get('/users/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.company).toBeDefined();\n            expect(res.body.company.projects).toBeDefined();\n            done();\n          });\n      });\n      it('should return joined entity with alias', (done) => {\n        const query = qb\n          .setFilter({ field: 'pr.id', operator: 'notnull' })\n          .setJoin({ field: 'company' })\n          .setJoin({ field: 'company.projects' })\n          .query();\n        request(server)\n          .get('/users2/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.company).toBeDefined();\n            expect(res.body.company.projects).toBeDefined();\n            done();\n          });\n      });\n      it('should return joined entity with ManyToMany pivot table', (done) => {\n        const query = qb.setJoin({ field: 'users' }).setJoin({ field: 'userProjects' }).query();\n        request(server)\n          .get('/projects/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.users).toBeDefined();\n            expect(res.body.users.length).toBe(2);\n            expect(res.body.users[0].id).toBe(1);\n            expect(res.body.userProjects).toBeDefined();\n            expect(res.body.userProjects.length).toBe(2);\n            expect(res.body.userProjects[0].review).toBe('User project 1 1');\n            done();\n          });\n      });\n    });\n\n    describe('#query composite key join', () => {\n      it('should return joined relation', (done) => {\n        const query = qb.setJoin({ field: 'userLicenses' }).query();\n        request(server)\n          .get('/users/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.userLicenses).toBeDefined();\n            done();\n          });\n      });\n    });\n\n    describe('#sort', () => {\n      it('should sort by field', async () => {\n        const query = qb.sortBy({ field: 'id', order: 'DESC' }).query();\n        const res = await request(server).get('/users').query(query).expect(200);\n        expect(res.body[1].id).toBeLessThan(res.body[0].id);\n      });\n\n      it('should sort by nested field, 1', async () => {\n        const query = qb\n          .setFilter({ field: 'company.id', operator: 'notnull' })\n          .setJoin({ field: 'company' })\n          .sortBy({ field: 'company.id', order: 'DESC' })\n          .query();\n        const res = await request(server).get('/users').query(query).expect(200);\n        expect(res.body[res.body.length - 1].company.id).toBeLessThan(res.body[0].company.id);\n      });\n\n      it('should sort by nested field, 2', async () => {\n        const query = qb\n          .setFilter({ field: 'id', operator: 'eq', value: 1 })\n          .setFilter({ field: 'company.id', operator: 'notnull' })\n          .setFilter({ field: 'projects.id', operator: 'notnull' })\n          .setJoin({ field: 'company' })\n          .setJoin({ field: 'company.projects' })\n          .sortBy({ field: 'projects.id', order: 'DESC' })\n          .query();\n        const res = await request(server).get('/users').query(query).expect(200);\n        expect(res.body[0].company.projects[1].id).toBeLessThan(res.body[0].company.projects[0].id);\n      });\n\n      it('should sort by nested field, 3', async () => {\n        const query = qb\n          .setFilter({ field: 'id', operator: 'eq', value: 1 })\n          .setFilter({ field: 'company.id', operator: 'notnull' })\n          .setFilter({ field: 'projects.id', operator: 'notnull' })\n          .setJoin({ field: 'company' })\n          .setJoin({ field: 'company.projects' })\n          .sortBy({ field: 'company.projects.id', order: 'DESC' })\n          .query();\n        const res = await request(server).get('/users').query(query).expect(200);\n        expect(res.body[0].company.projects[1].id).toBeLessThan(res.body[0].company.projects[0].id);\n      });\n\n      it('should throw 400 if SQL injection has been detected', (done) => {\n        const query = qb\n          .sortBy({\n            field: ' ASC; SELECT CAST( version() AS INTEGER); --',\n            order: 'DESC',\n          })\n          .query();\n\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(400);\n            done();\n          });\n      });\n    });\n\n    describe('#search', () => {\n      const projects2 = () => request(server).get('/projects2');\n      const projects3 = () => request(server).get('/projects3');\n      const projects4 = () => request(server).get('/projects4');\n\n      it('should return with search, 1', async () => {\n        const query = qb.search({ id: 1 }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 2', async () => {\n        const query = qb.search({ id: 1, name: 'Project1' }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 3', async () => {\n        const query = qb.search({ id: 1, name: { $eq: 'Project1' } }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 4', async () => {\n        const query = qb.search({ name: { $eq: 'Project1' } }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 5', async () => {\n        const query = qb.search({ id: { $notnull: true, $eq: 1 } }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 6', async () => {\n        const query = qb.search({ id: { $or: { $isnull: true, $eq: 1 } } }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 7', async () => {\n        const query = qb.search({ id: { $or: { $eq: 1 } } }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 8', async () => {\n        const query = qb.search({ id: { $notnull: true, $or: { $eq: 1, $in: [30, 31] } } }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 9', async () => {\n        const query = qb.search({ id: { $notnull: true, $or: { $eq: 1 } } }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(1);\n      });\n      it('should return with search, 10', async () => {\n        const query = qb.search({ id: null }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(0);\n      });\n      it('should return with search, 11', async () => {\n        const query = qb.search({ $and: [{ id: { $notin: [5, 6, 7, 8, 9, 10] } }, { isActive: true }] }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(4);\n      });\n      it('should return with search, 12', async () => {\n        const query = qb.search({ $and: [{ id: { $notin: [5, 6, 7, 8, 9, 10] } }] }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(14);\n      });\n      it('should return with search, 13', async () => {\n        const query = qb.search({ $or: [{ id: 54 }] }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(0);\n      });\n      it('should return with search, 14', async () => {\n        const query = qb.search({ $or: [{ id: 54 }, { id: 33 }, { id: { $in: [1, 2] } }] }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(2);\n        expect(res.body[0].id).toBe(1);\n        expect(res.body[1].id).toBe(2);\n      });\n      it('should return with search, 15', async () => {\n        const query = qb.search({ $or: [{ id: 54 }], name: 'Project1' }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(0);\n      });\n      it('should return with search, 16', async () => {\n        const query = qb.search({ $or: [{ isActive: false }, { id: 3 }], name: 'Project3' }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(3);\n      });\n      it('should return with search, 17', async () => {\n        const query = qb.search({ $or: [{ isActive: false }, { id: { $eq: 3 } }], name: 'Project3' }).query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(3);\n      });\n      it('should return with search, 17', async () => {\n        const query = qb\n          .search({\n            $or: [{ isActive: false }, { id: { $eq: 3 } }],\n            name: { $eq: 'Project3' },\n          })\n          .query();\n        const res = await projects2().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(3);\n      });\n      it('should return with default filter, 1', async () => {\n        const query = qb.search({ name: 'Project11' }).query();\n        const res = await projects3().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(11);\n      });\n      it('should return with default filter, 2', async () => {\n        const query = qb.search({ name: 'Project1' }).query();\n        const res = await projects3().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(0);\n      });\n      it('should return with default filter, 3', async () => {\n        const query = qb.search({ name: 'Project2' }).query();\n        const res = await projects4().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].id).toBe(2);\n      });\n      it('should return with default filter, 4', async () => {\n        const query = qb.search({ name: 'Project11' }).query();\n        const res = await projects4().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(0);\n      });\n      it('should return with $eqL search operator', async () => {\n        const query = qb.search({ name: { $eqL: 'project1' } }).query();\n        const res = await projects4().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n      });\n      it('should return with $neL search operator', async () => {\n        const query = qb.search({ name: { $neL: 'project1' } }).query();\n        const res = await projects4().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(9);\n      });\n      it('should return with $startsL search operator', async () => {\n        const query = qb.search({ email: { $startsL: '2' } }).query();\n        const res = await request(server).get('/users').query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(3);\n      });\n      it('should return with $endsL search operator', async () => {\n        const query = qb.search({ domain: { $endsL: 'AiN10' } }).query();\n        const res = await request(server).get('/companies').query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(1);\n        expect(res.body[0].domain).toBe('Domain10');\n      });\n      it('should return with $contL search operator', async () => {\n        const query = qb.search({ email: { $contL: '1@' } }).query();\n        const res = await request(server).get('/users').query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(3);\n      });\n      it('should return with $exclL search operator', async () => {\n        const query = qb.search({ email: { $exclL: '1@' } }).query();\n        const res = await request(server).get('/users').query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(18);\n      });\n      it('should return with $inL search operator', async () => {\n        const query = qb.search({ name: { $inL: ['name2', 'name3'] } }).query();\n        const res = await request(server).get('/companies').query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(2);\n      });\n      it('should return with $notinL search operator', async () => {\n        const query = qb.search({ name: { $notinL: ['project7', 'project8', 'project9'] } }).query();\n        const res = await projects4().query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(7);\n      });\n      it('should search by display column name, but use dbName in sql query', async () => {\n        const query = qb.search({ revisionId: 2 }).query();\n        const res = await request(server).get('/notes').query(query).expect(200);\n        expect(res.body).toBeArrayOfSize(2);\n        expect(res.body[0].revisionId).toBe(2);\n        expect(res.body[1].revisionId).toBe(2);\n      });\n    });\n\n    describe('#update', () => {\n      it('should update company id of project', async () => {\n        await request(server).patch('/projects/18').send({ companyId: 10 }).expect(200);\n\n        const modified = await request(server).get('/projects/18').expect(200);\n\n        expect(modified.body.companyId).toBe(10);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud-typeorm/test/c.basic-crud.spec.ts",
    "content": "import { Controller, INestApplication } from '@nestjs/common';\nimport { APP_FILTER } from '@nestjs/core';\nimport { Test } from '@nestjs/testing';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n\nimport { Crud } from '@nestjsx/crud';\nimport { RequestQueryBuilder } from '@nestjsx/crud-request';\nimport * as request from 'supertest';\nimport { Company } from '../../../integration/crud-typeorm/companies';\nimport { Device } from '../../../integration/crud-typeorm/devices';\nimport { withCache } from '../../../integration/crud-typeorm/orm.config';\nimport { Project } from '../../../integration/crud-typeorm/projects';\nimport { User } from '../../../integration/crud-typeorm/users';\nimport { UserProfile } from '../../../integration/crud-typeorm/users-profiles';\nimport { HttpExceptionFilter } from '../../../integration/shared/https-exception.filter';\nimport { CompaniesService } from './__fixture__/companies.service';\nimport { UsersService } from './__fixture__/users.service';\nimport { DevicesService } from './__fixture__/devices.service';\n\nconst isMysql = process.env.TYPEORM_CONNECTION === 'mysql';\n\n// tslint:disable:max-classes-per-file no-shadowed-variable\ndescribe('#crud-typeorm', () => {\n  describe('#basic crud using alwaysPaginate default respects global limit', () => {\n    let app: INestApplication;\n    let server: any;\n    let qb: RequestQueryBuilder;\n    let service: CompaniesService;\n\n    @Crud({\n      model: { type: Company },\n      query: {\n        alwaysPaginate: true,\n        limit: 3,\n      },\n    })\n    @Controller('companies0')\n    class CompaniesController0 {\n      constructor(public service: CompaniesService) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        imports: [TypeOrmModule.forRoot(withCache), TypeOrmModule.forFeature([Company])],\n        controllers: [CompaniesController0],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, CompaniesService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n      service = app.get<CompaniesService>(CompaniesService);\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    beforeEach(() => {\n      qb = RequestQueryBuilder.create();\n    });\n\n    afterAll(async () => {\n      await app.close();\n    });\n\n    describe('#getAllBase', () => {\n      it('should return an array of all entities', (done) => {\n        request(server)\n          .get('/companies0')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.data.length).toBe(3);\n            expect(res.body.page).toBe(1);\n            done();\n          });\n      });\n    });\n  });\n\n  describe('#basic crud using alwaysPaginate default', () => {\n    let app: INestApplication;\n    let server: any;\n    let qb: RequestQueryBuilder;\n    let service: CompaniesService;\n\n    @Crud({\n      model: { type: Company },\n      query: { alwaysPaginate: true },\n    })\n    @Controller('companies')\n    class CompaniesController {\n      constructor(public service: CompaniesService) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        imports: [TypeOrmModule.forRoot(withCache), TypeOrmModule.forFeature([Company])],\n        controllers: [CompaniesController],\n        providers: [{ provide: APP_FILTER, useClass: HttpExceptionFilter }, CompaniesService],\n      }).compile();\n\n      app = fixture.createNestApplication();\n      service = app.get<CompaniesService>(CompaniesService);\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    beforeEach(() => {\n      qb = RequestQueryBuilder.create();\n    });\n\n    afterAll(async () => {\n      await app.close();\n    });\n\n    describe('#getAllBase', () => {\n      it('should return an array of all entities', (done) => {\n        request(server)\n          .get('/companies')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.data.length).toBe(9);\n            expect(res.body.page).toBe(1);\n            done();\n          });\n      });\n      it('should return an entities with limit', (done) => {\n        const query = qb.setLimit(5).query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.data.length).toBe(5);\n            expect(res.body.page).toBe(1);\n            done();\n          });\n      });\n      it('should return an entities with limit and page', (done) => {\n        const query = qb.setLimit(3).setPage(1).sortBy({ field: 'id', order: 'DESC' }).query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.data.length).toBe(3);\n            expect(res.body.count).toBe(3);\n            expect(res.body.page).toBe(1);\n            done();\n          });\n      });\n    });\n  });\n\n  describe('#basic crud', () => {\n    let app: INestApplication;\n    let server: any;\n    let qb: RequestQueryBuilder;\n    let service: CompaniesService;\n\n    @Crud({\n      model: { type: Company },\n      query: {\n        softDelete: true,\n      },\n    })\n    @Controller('companies')\n    class CompaniesController {\n      constructor(public service: CompaniesService) {}\n    }\n\n    @Crud({\n      model: { type: User },\n      params: {\n        companyId: {\n          field: 'companyId',\n          type: 'number',\n        },\n        id: {\n          field: 'id',\n          type: 'number',\n          primary: true,\n        },\n      },\n      routes: {\n        deleteOneBase: {\n          returnDeleted: true,\n        },\n      },\n      query: {\n        persist: ['isActive'],\n        cache: 10,\n      },\n      validation: {\n        transform: true,\n      },\n    })\n    @Controller('companies/:companyId/users')\n    class UsersController {\n      constructor(public service: UsersService) {}\n    }\n\n    @Crud({\n      model: { type: User },\n      query: {\n        join: {\n          profile: {\n            eager: true,\n            required: true,\n          },\n        },\n      },\n    })\n    @Controller('/users2')\n    class UsersController2 {\n      constructor(public service: UsersService) {}\n    }\n\n    @Crud({\n      model: { type: User },\n      query: {\n        join: {\n          profile: {\n            eager: true,\n          },\n        },\n      },\n    })\n    @Controller('/users3')\n    class UsersController3 {\n      constructor(public service: UsersService) {}\n    }\n\n    @Crud({\n      model: { type: User },\n      params: {\n        companyId: { field: 'companyId', type: 'number', primary: true },\n        profileId: { field: 'profileId', type: 'number', primary: true },\n      },\n    })\n    @Controller('users4')\n    class UsersController4 {\n      constructor(public service: UsersService) {}\n    }\n\n    @Crud({\n      model: { type: Device },\n      params: {\n        deviceKey: {\n          field: 'deviceKey',\n          type: 'uuid',\n          primary: true,\n        },\n      },\n      routes: {\n        createOneBase: {\n          returnShallow: true,\n        },\n      },\n    })\n    @Controller('devices')\n    class DevicesController {\n      constructor(public service: DevicesService) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        imports: [\n          TypeOrmModule.forRoot({ ...withCache, logging: false }),\n          TypeOrmModule.forFeature([Company, Project, User, UserProfile, Device]),\n        ],\n        controllers: [\n          CompaniesController,\n          UsersController,\n          UsersController2,\n          UsersController3,\n          UsersController4,\n          DevicesController,\n        ],\n        providers: [\n          { provide: APP_FILTER, useClass: HttpExceptionFilter },\n          CompaniesService,\n          UsersService,\n          DevicesService,\n        ],\n      }).compile();\n\n      app = fixture.createNestApplication();\n      service = app.get<CompaniesService>(CompaniesService);\n\n      await app.init();\n      server = app.getHttpServer();\n    });\n\n    beforeEach(() => {\n      qb = RequestQueryBuilder.create();\n    });\n\n    afterAll(async () => {\n      await app.close();\n    });\n\n    describe('#find', () => {\n      it('should return entities', async () => {\n        const data = await service.find();\n        expect(data.length).toBe(9);\n      });\n    });\n\n    describe('#findOne', () => {\n      it('should return one entity', async () => {\n        const data = await service.findOne({ where: { id: 1 } });\n        expect(data.id).toBe(1);\n      });\n    });\n\n    describe('#count', () => {\n      it('should return number', async () => {\n        const data = await service.count();\n        expect(typeof data).toBe('number');\n      });\n    });\n\n    describe('#getAllBase', () => {\n      it('should return an array of all entities', (done) => {\n        request(server)\n          .get('/companies?include_deleted=1')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(10);\n            done();\n          });\n      });\n      it('should return an entities with limit', (done) => {\n        const query = qb.setLimit(5).query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.length).toBe(5);\n            done();\n          });\n      });\n      it('should return an entities with limit and page', (done) => {\n        const query = qb.setLimit(3).setPage(1).sortBy({ field: 'id', order: 'DESC' }).query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.data.length).toBe(3);\n            expect(res.body.count).toBe(3);\n            expect(res.body.total).toBe(9);\n            expect(res.body.page).toBe(1);\n            expect(res.body.pageCount).toBe(3);\n            done();\n          });\n      });\n      it('should return an entities with offset', (done) => {\n        const queryObj = qb.setOffset(3);\n        if (isMysql) {\n          queryObj.setLimit(10);\n        }\n        const query = queryObj.query();\n        request(server)\n          .get('/companies')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            if (isMysql) {\n              expect(res.body.count).toBe(6);\n              expect(res.body.data.length).toBe(6);\n            } else {\n              expect(res.body.length).toBe(6);\n            }\n            done();\n          });\n      });\n    });\n\n    describe('#getOneBase', () => {\n      it('should return status 404', (done) => {\n        request(server)\n          .get('/companies/333')\n          .end((_, res) => {\n            expect(res.status).toBe(404);\n            done();\n          });\n      });\n      it('should return status 404 for deleted entity', (done) => {\n        request(server)\n          .get('/companies/9')\n          .end((_, res) => {\n            expect(res.status).toBe(404);\n            done();\n          });\n      });\n      it('should return a deleted entity if include_deleted query param is specified', (done) => {\n        request(server)\n          .get('/companies/9?include_deleted=1')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.id).toBe(9);\n            done();\n          });\n      });\n      it('should return an entity, 1', (done) => {\n        request(server)\n          .get('/companies/1')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.id).toBe(1);\n            done();\n          });\n      });\n      it('should return an entity, 2', (done) => {\n        const query = qb.select(['domain']).query();\n        request(server)\n          .get('/companies/1')\n          .query(query)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.id).toBe(1);\n            expect(res.body.domain).toBeTruthy();\n            done();\n          });\n      });\n      it('should return an entity with compound key', (done) => {\n        request(server)\n          .get('/users4/1/5')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.id).toBe(5);\n            done();\n          });\n      });\n      it('should return an entity with and set cache', (done) => {\n        request(server)\n          .get('/companies/1/users/1')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.id).toBe(1);\n            expect(res.body.companyId).toBe(1);\n            done();\n          });\n      });\n\n      it('should return an entity with its embedded entity properties', (done) => {\n        request(server)\n          .get('/companies/1/users/1')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.id).toBe(1);\n            expect(res.body.name.first).toBe('firstname1');\n            expect(res.body.name.last).toBe('lastname1');\n            done();\n          });\n      });\n    });\n\n    describe('#createOneBase', () => {\n      it('should return status 400', (done) => {\n        request(server)\n          .post('/companies')\n          .send('')\n          .end((_, res) => {\n            expect(res.status).toBe(400);\n            done();\n          });\n      });\n      it('should return saved entity', (done) => {\n        const dto = {\n          name: 'test0',\n          domain: 'test0',\n        };\n        request(server)\n          .post('/companies')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(201);\n            expect(res.body.id).toBeTruthy();\n            done();\n          });\n      });\n      it('should return saved entity with param', (done) => {\n        const dto: any = {\n          email: 'test@test.com',\n          isActive: true,\n          name: {\n            first: 'test',\n            last: 'last',\n          },\n          profile: {\n            name: 'testName',\n          },\n        };\n        request(server)\n          .post('/companies/1/users')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(201);\n            expect(res.body.id).toBeTruthy();\n            expect(res.body.companyId).toBe(1);\n            done();\n          });\n      });\n      it('should return with `returnShallow`', (done) => {\n        const dto: any = { description: 'returnShallow is true' };\n        request(server)\n          .post('/devices')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(201);\n            expect(res.body.deviceKey).toBeTruthy();\n            expect(res.body.description).toBeTruthy();\n            done();\n          });\n      });\n    });\n\n    describe('#createManyBase', () => {\n      it('should return status 400', (done) => {\n        const dto = { bulk: [] };\n        request(server)\n          .post('/companies/bulk')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(400);\n            done();\n          });\n      });\n      it('should return created entities', (done) => {\n        const dto = {\n          bulk: [\n            {\n              name: 'test1',\n              domain: 'test1',\n            },\n            {\n              name: 'test2',\n              domain: 'test2',\n            },\n          ],\n        };\n        request(server)\n          .post('/companies/bulk')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(201);\n            expect(res.body[0].id).toBeTruthy();\n            expect(res.body[1].id).toBeTruthy();\n            done();\n          });\n      });\n    });\n\n    describe('#updateOneBase', () => {\n      it('should return status 404', (done) => {\n        const dto = { name: 'updated0' };\n        request(server)\n          .patch('/companies/333')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(404);\n            done();\n          });\n      });\n      it('should return updated entity, 1', (done) => {\n        const dto = { name: 'updated0' };\n        request(server)\n          .patch('/companies/1')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.name).toBe('updated0');\n            done();\n          });\n      });\n      it('should return updated entity, 2', (done) => {\n        const dto = { isActive: false, companyId: 5 };\n        request(server)\n          .patch('/companies/1/users/22')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.isActive).toBe(false);\n            expect(res.body.companyId).toBe(1);\n            done();\n          });\n      });\n    });\n\n    describe('#replaceOneBase', () => {\n      it('should create entity', (done) => {\n        const dto = { name: 'updated0', domain: 'domain0' };\n        request(server)\n          .put('/companies/333')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.name).toBe('updated0');\n            done();\n          });\n      });\n      it('should return updated entity, 1', (done) => {\n        const dto = { name: 'updated0' };\n        request(server)\n          .put('/companies/1')\n          .send(dto)\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.name).toBe('updated0');\n            done();\n          });\n      });\n    });\n\n    describe('#deleteOneBase', () => {\n      it('should return status 404', (done) => {\n        request(server)\n          .delete('/companies/3333')\n          .end((_, res) => {\n            expect(res.status).toBe(404);\n            done();\n          });\n      });\n      it('should softly delete entity', (done) => {\n        request(server)\n          .delete('/companies/5')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            done();\n          });\n      });\n      it('should not return softly deleted entity', (done) => {\n        request(server)\n          .get('/companies/5')\n          .end((_, res) => {\n            expect(res.status).toBe(404);\n            done();\n          });\n      });\n      it('should recover softly deleted entity', (done) => {\n        request(server)\n          .patch('/companies/5/recover')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            done();\n          });\n      });\n      it('should return recovered entity', (done) => {\n        request(server)\n          .get('/companies/5')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.id).toBe(5);\n            done();\n          });\n      });\n      it('should return deleted entity', (done) => {\n        request(server)\n          .delete('/companies/1/users/22')\n          .end((_, res) => {\n            expect(res.status).toBe(200);\n            expect(res.body.id).toBe(22);\n            expect(res.body.companyId).toBe(1);\n            done();\n          });\n      });\n    });\n\n    describe('join options: required', () => {\n      const users2 = () => request(server).get('/users2/21');\n      const users3 = () => request(server).get('/users3/21');\n\n      it('should return status 404', async () => {\n        await users2().expect(404);\n      });\n\n      it('should return status 200', async () => {\n        const res = await users3().expect(200);\n        expect(res.body.id).toBe(21);\n        expect(res.body.profile).toBe(null);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud-typeorm/test/d.crud-auth.spec.ts",
    "content": "import { Controller, INestApplication, Injectable, CanActivate, ExecutionContext } from '@nestjs/common';\nimport { APP_FILTER, APP_GUARD } from '@nestjs/core';\nimport { Test } from '@nestjs/testing';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n\nimport { Crud, CrudAuth } from '@nestjsx/crud';\nimport * as request from 'supertest';\nimport { withCache } from '../../../integration/crud-typeorm/orm.config';\nimport { User } from '../../../integration/crud-typeorm/users';\nimport { UserProfile } from '../../../integration/crud-typeorm/users-profiles';\nimport { Project } from '../../../integration/crud-typeorm/projects';\nimport { HttpExceptionFilter } from '../../../integration/shared/https-exception.filter';\nimport { UsersService } from './__fixture__/users.service';\nimport { ProjectsService } from './__fixture__/projects.service';\n\ndescribe('#crud-typeorm', () => {\n  describe('#CrudAuth', () => {\n    const USER_REQUEST_KEY = 'user';\n    let app: INestApplication;\n    let server: request.SuperTest<request.Test>;\n\n    @Injectable()\n    class AuthGuard implements CanActivate {\n      constructor(private usersService: UsersService) {}\n\n      async canActivate(ctx: ExecutionContext): Promise<boolean> {\n        const req = ctx.switchToHttp().getRequest();\n        req[USER_REQUEST_KEY] = await this.usersService.findOne({ where: { id: 1 } });\n\n        return true;\n      }\n    }\n\n    @Crud({\n      model: {\n        type: User,\n      },\n      routes: {\n        only: ['getOneBase', 'updateOneBase'],\n      },\n      params: {\n        id: {\n          primary: true,\n          disabled: true,\n        },\n      },\n    })\n    @CrudAuth({\n      property: USER_REQUEST_KEY,\n      filter: (user: User) => ({\n        id: user.id,\n      }),\n      persist: (user: User) => ({\n        email: user.email,\n      }),\n    })\n    @Controller('me')\n    class MeController {\n      constructor(public service: UsersService) {}\n    }\n\n    @Crud({\n      model: {\n        type: Project,\n      },\n      routes: {\n        only: ['createOneBase', 'deleteOneBase'],\n      },\n    })\n    @CrudAuth({\n      property: USER_REQUEST_KEY,\n      filter: (user: User) => ({\n        companyId: user.companyId,\n      }),\n      persist: (user: User) => ({\n        companyId: user.companyId,\n      }),\n    })\n    @Controller('projects')\n    class ProjectsController {\n      constructor(public service: ProjectsService) {}\n    }\n\n    beforeAll(async () => {\n      const fixture = await Test.createTestingModule({\n        imports: [\n          TypeOrmModule.forRoot({ ...withCache, logging: false }),\n          TypeOrmModule.forFeature([User, UserProfile, Project]),\n        ],\n        controllers: [MeController, ProjectsController],\n        providers: [\n          {\n            provide: APP_GUARD,\n            useClass: AuthGuard,\n          },\n          {\n            provide: APP_FILTER,\n            useClass: HttpExceptionFilter,\n          },\n          UsersService,\n          ProjectsService,\n        ],\n      }).compile();\n\n      app = fixture.createNestApplication();\n\n      await app.init();\n      server = request(app.getHttpServer());\n    });\n\n    afterAll(async () => {\n      await app.close();\n    });\n\n    describe('#getOneBase', () => {\n      it('should return a user with id 1', async () => {\n        const res = await server.get('/me').expect(200);\n        expect(res.body.id).toBe(1);\n      });\n    });\n\n    describe('#updateOneBase', () => {\n      it('should update user with auth persist, 1', async () => {\n        const res = await server\n          .patch('/me')\n          .send({\n            email: 'some@dot.com',\n            isActive: false,\n          })\n          .expect(200);\n        expect(res.body.id).toBe(1);\n        expect(res.body.email).toBe('1@email.com');\n        expect(res.body.isActive).toBe(false);\n      });\n      it('should update user with auth persist, 1', async () => {\n        const res = await server\n          .patch('/me')\n          .send({\n            email: 'some@dot.com',\n            isActive: true,\n          })\n          .expect(200);\n        expect(res.body.id).toBe(1);\n        expect(res.body.email).toBe('1@email.com');\n        expect(res.body.isActive).toBe(true);\n      });\n    });\n\n    describe('#createOneBase', () => {\n      it('should create an entity with auth persist', async () => {\n        const res = await server\n          .post('/projects')\n          .send({\n            name: 'Test',\n            description: 'foo',\n            isActive: false,\n            companyId: 10,\n          })\n          .expect(201);\n        expect(res.body.companyId).toBe(1);\n      });\n    });\n\n    describe('#deleteOneBase', () => {\n      it('should delete an entity with auth filter', async () => {\n        const res = await server.delete('/projects/21').expect(200);\n      });\n      it('should throw an error with auth filter', async () => {\n        const res = await server.delete('/projects/20').expect(404);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "packages/crud-typeorm/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"rootDir\": \"src\",\n    \"outDir\": \"lib\"\n  },\n  \"include\": [\"src\"],\n  \"exclude\": [\"lib\"],\n  \"references\": [\n    { \"path\": \"../util\" },\n    { \"path\": \"../crud-request\" },\n    { \"path\": \"../crud\" }\n  ]\n}\n"
  },
  {
    "path": "packages/util/README.md",
    "content": "<div align=\"center\">\n  <h1>CRUD (@nestjsx/util)</h1>\n</div>\n<div align=\"center\">\n  <strong>for RESTful APIs built with NestJs</strong>\n</div>\n\n<br />\n\n<div align=\"center\">\n  <a href=\"https://travis-ci.org/nestjsx/crud\">\n    <img src=\"https://github.com/nestjsx/crud/workflows/Tests/badge.svg\" alt=\"Build\" />\n  </a>\n  <a href=\"https://coveralls.io/github/nestjsx/crud?branch=master\">\n    <img src=\"https://coveralls.io/repos/github/nestjsx/crud/badge.svg\" alt=\"Coverage\" />\n  </a>\n  <a href=\"https://github.com/nestjsx/crud/blob/master/LICENSE\">\n    <img src=\"https://img.shields.io/github/license/nestjsx/crud.svg\" alt=\"License\" />\n  </a>\n  <a href=\"https://www.npmjs.com/package/@nestjsx/crud\">\n    <img src=\"https://img.shields.io/npm/v/@nestjsx/crud.svg\" alt=\"npm version\" />\n  </a>\n  <a href=\"https://www.npmjs.com/org/nestjsx\">\n    <img src=\"https://img.shields.io/npm/dm/@nestjsx/crud.svg\" alt=\"npm downloads\" />\n  </a>\n  <a href=\"https://npm.packagequality.com/#?package=@nestjsx%2Fcrud\">\n    <img src=\"https://npm.packagequality.com/shield/%40nestjsx%2Fcrud.svg\" alt=\"Package Quality\" />\n  </a>\n  <a href=\"https://renovatebot.com/\">\n    <img src=\"https://img.shields.io/badge/renovate-enabled-brightgreen.svg\" alt=\"Renovate\" />\n  </a>\n  <a href=\"http://makeapullrequest.com\">\n    <img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\" alt=\"PRs welcome\" />\n  </a>\n  <a href=\"https://github.com/marmelab/awesome-rest#nodejs\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-rest.svg?sanitize=true\" alt=\"Awesome REST\" />\n  </a>\n  <a href=\"https://github.com/juliandavidmr/awesome-nestjs#components--libraries\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/awesome-nest.svg?sanitize=true\" alt=\"Awesome Nest\" />\n  </a>\n  <a href=\"https://github.com/nestjs/nest\">\n    <img src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/nest-powered.svg?sanitize=true\" alt=\"Nest Powered\" />\n  </a>\n  <a href=\"#individuals\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/backers/badge.svg\" />\n  </a>\n  <a href=\"#organizations\" alt=\"Sponsors on Open Collective\">\n    <img src=\"https://opencollective.com/nestjsx/sponsors/badge.svg\" />\n  </a> \n</div>\n\n<div align=\"center\">\n  <sub>Built by\n  <a href=\"https://twitter.com/MichaelYali\">@MichaelYali</a> and\n  <a href=\"https://github.com/nestjsx/crud/graphs/contributors\">\n    Contributors\n  </a>\n</div>\n\n<br />\n\nWe believe that everyone who's working with NestJs and building some RESTful services and especially some CRUD functionality will find `@nestjsx/crud` microframework very useful.\n\n## Features\n\n<img align=\"right\" src=\"https://raw.githubusercontent.com/nestjsx/crud/master/img/crud-usage2.png\" alt=\"CRUD usage\" />\n\n- Super easy to install and start using the full-featured controllers and services :point_right:\n\n- DB and service agnostic extendable CRUD controllers\n\n- Reach query parsing with filtering, pagination, sorting, relations, nested relations, cache, etc.\n\n- Framework agnostic package with query builder for a frontend usage\n\n- Query, path params and DTOs validation included\n\n- Overriding controller methods with ease\n\n- Tiny config (including globally)\n\n- Additional helper decorators\n\n- Swagger documentation\n\n## Packages\n\n- [**@nestjsx/crud**](https://www.npmjs.com/package/@nestjsx/crud) - core package which provides `@Crud()` decorator for endpoints generation, global configuration, validation, helper decorators ([docs](https://github.com/nestjsx/crud/wiki/Controllers#description))\n- [**@nestjsx/crud-request**](https://www.npmjs.com/package/@nestjsx/crud-request) - request builder/parser package which provides `RequestQueryBuilder` class for a frontend usage and `RequestQueryParser` that is being used internally for handling and validating query/path params on a backend side ([docs](https://github.com/nestjsx/crud/wiki/Requests#frontend-usage))\n- [**@nestjsx/crud-typeorm**](https://www.npmjs.com/package/@nestjsx/crud-typeorm) - TypeORM package which provides base `TypeOrmCrudService` with methods for CRUD database operations ([docs](https://github.com/nestjsx/crud/wiki/ServiceTypeorm))\n\n## Documentation\n\n- [General Information](https://github.com/nestjsx/crud/wiki#why)\n- [CRUD Controllers](https://github.com/nestjsx/crud/wiki/Controllers#description)\n- [CRUD ORM Services](https://github.com/nestjsx/crud/wiki/Services#description)\n- [Handling Requests](https://github.com/nestjsx/crud/wiki/Requests#description)\n\n## Support\n\nAny support is welcome. At least you can give us a star.\n\n## Contributors\n\n### Code Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].\n<a href=\"https://github.com/nestjsx/crud/graphs/contributors\"><img src=\"https://opencollective.com/nestjsx/contributors.svg?width=890&button=false\" /></a>\n\n### Financial Contributors\n\nBecome a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/nestjsx#backer)]\n\n#### Individuals\n\n<a href=\"https://opencollective.com/nestjsx#backers\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/backers.svg?width=890&button=false\"></a>\n\n#### Organizations\n\nSupport this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/nestjsx#sponsor)]\n\n<a href=\"https://opencollective.com/nestjsx/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/nestjsx/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/nestjsx/sponsor/9/avatar.svg\"></a>\n\n## License\n\n[MIT](LICENSE)\n"
  },
  {
    "path": "packages/util/package.json",
    "content": "{\n  \"name\": \"@nestjsx/util\",\n  \"description\": \"NestJs CRUD for RESTful APIs - util\",\n  \"version\": \"5.0.0-alpha.3\",\n  \"license\": \"MIT\",\n  \"main\": \"lib/index.js\",\n  \"typings\": \"lib/index.d.ts\",\n  \"publishConfig\": {\n    \"access\": \"public\"\n  },\n  \"files\": [\n    \"lib\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/nestjsx/crud.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/nestjsx/crud/issues\"\n  },\n  \"keywords\": [\n    \"typescript\",\n    \"typeorm\",\n    \"nest\",\n    \"nestjs\",\n    \"rest\",\n    \"restful\",\n    \"api\",\n    \"crud\",\n    \"crud-generator\"\n  ],\n  \"author\": {\n    \"name\": \"Michael Yali\",\n    \"email\": \"mihon4ik@gmail.com\"\n  },\n  \"scripts\": {\n    \"build\": \"npx tsc -b\"\n  }\n}\n"
  },
  {
    "path": "packages/util/src/checks.util.ts",
    "content": "import { objKeys } from './obj.util';\n\nexport const isUndefined = (val: any): boolean => typeof val === 'undefined';\nexport const isNull = (val: any): boolean => val === null;\nexport const isNil = (val: any): boolean => isUndefined(val) || isNull(val);\nexport const isString = (val: any): boolean => typeof val === 'string';\nexport const hasLength = (val: any): boolean => val.length > 0;\nexport const isStringFull = (val: any): boolean => isString(val) && hasLength(val);\nexport const isArrayFull = (val: any): boolean => Array.isArray(val) && hasLength(val);\nexport const isArrayStrings = (val: any): boolean =>\n  isArrayFull(val) && (val as string[]).every((v) => isStringFull(v));\nexport const isObject = (val: any): boolean => typeof val === 'object' && !isNull(val);\nexport const isObjectFull = (val: any) => isObject(val) && hasLength(objKeys(val));\nexport const isNumber = (val: any): boolean =>\n  typeof val === 'number' && !Number.isNaN(val) && Number.isFinite(val);\nexport const isEqual = (val: any, eq: any): boolean => val === eq;\nexport const isFalse = (val: any): boolean => val === false;\nexport const isTrue = (val: any): boolean => val === true;\nexport const isIn = (val: any, arr: any[] = []): boolean =>\n  arr.some((o) => isEqual(val, o));\nexport const isBoolean = (val: any): boolean => typeof val === 'boolean';\nexport const isNumeric = (val: any): boolean => /^[+-]?([0-9]*[.])?[0-9]+$/.test(val);\nexport const isDateString = (val: any): boolean =>\n  isStringFull(val) &&\n  /^\\d{4}-[01]\\d-[0-3]\\d(?:T[0-2]\\d:[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|[-+][0-2]\\d(?::?[0-5]\\d)?)?)?$/g.test(\n    val,\n  );\nexport const isDate = (val: any): val is Date => val instanceof Date;\nexport const isValue = (val: any): boolean =>\n  isStringFull(val) || isNumber(val) || isBoolean(val) || isDate(val);\nexport const hasValue = (val: any): boolean =>\n  isArrayFull(val) ? (val as any[]).every((o) => isValue(o)) : isValue(val);\nexport const isFunction = (val: any): boolean => typeof val === 'function';\n"
  },
  {
    "path": "packages/util/src/index.ts",
    "content": "export * from './checks.util';\nexport * from './obj.util';\nexport * from './types';\n"
  },
  {
    "path": "packages/util/src/obj.util.ts",
    "content": "export const objKeys = (val: any): string[] => Object.keys(val);\nexport const getOwnPropNames = (val: any): string[] => Object.getOwnPropertyNames(val);\n"
  },
  {
    "path": "packages/util/src/types/class.type.ts",
    "content": "export type ClassType<T> = {\n  new (...args: any[]): T;\n};\n"
  },
  {
    "path": "packages/util/src/types/index.ts",
    "content": "export * from './class.type';\nexport * from './object-literal.type';\n"
  },
  {
    "path": "packages/util/src/types/object-literal.type.ts",
    "content": "export type ObjectLiteral = {\n  [key: string]: any;\n};\n"
  },
  {
    "path": "packages/util/test/checks.util.spec.ts",
    "content": "import {\n  hasLength,\n  hasValue,\n  isArrayFull,\n  isArrayStrings,\n  isBoolean,\n  isDate,\n  isDateString,\n  isEqual,\n  isFalse,\n  isFunction,\n  isIn,\n  isNil,\n  isNull,\n  isNumber,\n  isNumeric,\n  isObject,\n  isObjectFull,\n  isString,\n  isStringFull,\n  isTrue,\n  isUndefined,\n  isValue,\n} from '../src/';\n\ndescribe('#util', () => {\n  describe('#isUndefined', () => {\n    it('should return true', () => {\n      expect(isUndefined(undefined)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isUndefined(null)).toBe(false);\n    });\n  });\n\n  describe('#isNull', () => {\n    it('should reurn true', () => {\n      expect(isNull(null)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isNull(NaN)).toBe(false);\n    });\n  });\n\n  describe('#isNil', () => {\n    it('should return true', () => {\n      expect(isNil(null)).toBe(true);\n      expect(isNil(undefined)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isNil(NaN)).toBe(false);\n    });\n  });\n\n  describe('#isString', () => {\n    it('should return true', () => {\n      expect(isString('test')).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isString(null)).toBe(false);\n    });\n  });\n\n  describe('#hasLength', () => {\n    it('should return true', () => {\n      expect(hasLength('test')).toBe(true);\n      expect(hasLength([1])).toBe(true);\n    });\n    it('should return false', () => {\n      expect(hasLength({})).toBe(false);\n      expect(hasLength('')).toBe(false);\n      expect(hasLength([])).toBe(false);\n    });\n  });\n\n  describe('#isStringFull', () => {\n    it('should return true', () => {\n      expect(isStringFull('test')).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isStringFull('')).toBe(false);\n      expect(isStringFull([])).toBe(false);\n    });\n  });\n\n  describe('#isArrayFull', () => {\n    it('should return true', () => {\n      expect(isArrayFull([1])).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isArrayFull([])).toBe(false);\n    });\n  });\n\n  describe('#isArrayStrings', () => {\n    it('should return true', () => {\n      expect(isArrayStrings(['1', 'true'])).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isArrayStrings([])).toBe(false);\n    });\n  });\n\n  describe('#isObject', () => {\n    it('should return true', () => {\n      expect(isObject({})).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isObject(1)).toBe(false);\n      expect(isObject(null)).toBe(false);\n    });\n  });\n\n  describe('#isObjectFull', () => {\n    it('should return true', () => {\n      expect(isObjectFull({ foo: 1 })).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isObjectFull({})).toBe(false);\n    });\n  });\n\n  describe('#isNumber', () => {\n    it('should return true', () => {\n      expect(isNumber(1)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isNumber(true)).toBe(false);\n      expect(isNumber(NaN)).toBe(false);\n      expect(isNumber(Infinity)).toBe(false);\n    });\n  });\n\n  describe('#isEqual', () => {\n    it('should return true', () => {\n      expect(isEqual(1, 1)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isEqual(1, 2)).toBe(false);\n    });\n  });\n\n  describe('#isFalse', () => {\n    it('should return true', () => {\n      expect(isFalse(false)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isFalse(1)).toBe(false);\n    });\n  });\n\n  describe('#isTrue', () => {\n    it('should return true', () => {\n      expect(isTrue(true)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isTrue(1)).toBe(false);\n    });\n  });\n\n  describe('#isIn', () => {\n    it('should return true', () => {\n      expect(isIn(1, [1, 2])).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isIn(1, [])).toBe(false);\n      expect(isIn(1)).toBe(false);\n    });\n  });\n\n  describe('#isBoolean', () => {\n    it('should return true', () => {\n      for (const val of [true, false]) {\n        expect(isBoolean(val)).toBe(true);\n      }\n    });\n    it('should return false', () => {\n      for (const val of [1, null, undefined, {}, [], NaN]) {\n        expect(isBoolean(val)).toBe(false);\n      }\n    });\n  });\n\n  describe('#isNumeric', () => {\n    it('should return true', () => {\n      for (const val of [1, 0, '123', '-9.6']) {\n        expect(isNumeric(val)).toBe(true);\n      }\n    });\n    it('should return false', () => {\n      for (const val of ['', [], {}, null, undefined]) {\n        expect(isNumeric(val)).toBe(false);\n      }\n    });\n  });\n\n  describe('#isDateString', () => {\n    it('should return true', () => {\n      for (const val of [\n        '2019-06-19',\n        '2019-06-19T12:30:00',\n        '2019-06-19T12:30:00+0800',\n        '2019-06-19T12:30:00-08:00',\n        '2019-06-19T00:00:00Z',\n      ]) {\n        expect(isDateString(val)).toBe(true);\n      }\n    });\n    it('should return false', () => {\n      for (const val of ['product-123123', 'CG-7', '20190619', [], {}, null, undefined]) {\n        expect(isDateString(val)).toBe(false);\n      }\n    });\n  });\n\n  describe('#isDate', () => {\n    it('should return true', () => {\n      for (const val of [new Date()]) {\n        expect(isDate(val)).toBe(true);\n      }\n    });\n    it('should return false', () => {\n      for (const val of ['2019-06-19', [], {}, null, undefined]) {\n        expect(isDate(val)).toBe(false);\n      }\n    });\n  });\n\n  describe('#isValue', () => {\n    it('should return true', () => {\n      for (const val of [new Date(), 'test', -1, 0, true, false]) {\n        expect(isValue(val)).toBe(true);\n      }\n    });\n    it('should return false', () => {\n      for (const val of ['', [], {}, null, undefined]) {\n        expect(isValue(val)).toBe(false);\n      }\n    });\n  });\n\n  describe('#hasValue', () => {\n    it('should return true', () => {\n      expect(hasValue([new Date(), 'test', -1, 0, true, false])).toBe(true);\n      expect(hasValue(false)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(hasValue(['', [], {}, null, undefined])).toBe(false);\n      expect(hasValue(null)).toBe(false);\n    });\n  });\n\n  describe('#isFunction', () => {\n    it('should return true', () => {\n      expect(isFunction(Date)).toBe(true);\n    });\n    it('should return false', () => {\n      expect(isFunction(new Date())).toBe(false);\n    });\n  });\n});\n"
  },
  {
    "path": "packages/util/test/obj.util.spec.ts",
    "content": "import { objKeys, getOwnPropNames } from '../src';\n\ndescribe('#util', () => {\n  describe('#objKeys', () => {\n    it('should return array of strings', () => {\n      const obj = { foo: 1, bar: 1 };\n      const keys = ['foo', 'bar'];\n      expect(objKeys(obj)).toMatchObject(keys);\n    });\n  });\n\n  describe('#getOwnPropNames', () => {\n    it('should return own properties', () => {\n      class Parent {\n        foo = 1;\n      }\n      class Child extends Parent {\n        bar = 1;\n      }\n      const expected = ['foo', 'bar'];\n      expect(getOwnPropNames(new Child())).toMatchObject(expected);\n    });\n  });\n});\n"
  },
  {
    "path": "packages/util/tsconfig.json",
    "content": "{\n  \"extends\": \"../../tsconfig.json\",\n  \"compilerOptions\": {\n    \"rootDir\": \"src\",\n    \"outDir\": \"lib\"\n  },\n  \"include\": [\"src\"],\n  \"exclude\": [\"lib\"]\n}\n"
  },
  {
    "path": "tsconfig.eslint.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"include\": [\"packages\"]\n}\n"
  },
  {
    "path": "tsconfig.jest.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"removeComments\": false\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"declaration\": true,\n    \"noImplicitAny\": false,\n    \"noUnusedLocals\": false,\n    \"removeComments\": true,\n    \"noLib\": false,\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"target\": \"es2018\",\n    \"sourceMap\": true,\n    \"allowJs\": false,\n    \"skipLibCheck\": false,\n    \"composite\": true,\n    \"baseUrl\": \"./packages\",\n    \"paths\": {\n      \"@nestjsx/crud\": [\"crud/src\"],\n      \"@nestjsx/crud-typeorm\": [\"crud-typeorm/src\"],\n      \"@nestjsx/crud-request\": [\"crud-request/src\"],\n      \"@nestjsx/util\": [\"util/src\"],\n      \"@nestjsx/crud/*\": [\"crud/src/*\"],\n      \"@nestjsx/crud-typeorm/*\": [\"crud-typeorm/src/*\"],\n      \"@nestjsx/crud-request/*\": [\"crud-request/src/*\"],\n      \"@nestjsx/util/*\": [\"util/src/*\"]\n    }\n  },\n  \"references\": [\n    { \"path\": \"crud\" },\n    { \"path\": \"crud-typeorm\" },\n    { \"path\": \"crud-request\" },\n    { \"path\": \"util\" }\n  ]\n}\n"
  },
  {
    "path": "tslint.json",
    "content": "{\n  \"defaultSeverity\": \"error\",\n  \"extends\": [\"tslint:latest\", \"tslint-config-prettier\"],\n  \"jsRules\": {},\n  \"rules\": {\n    \"object-literal-sort-keys\": false,\n    \"member-access\": false,\n    \"no-implicit-dependencies\": false,\n    \"member-ordering\": false,\n    \"prefer-for-of\": false,\n    \"no-submodule-imports\": false,\n    \"interface-name\": false\n  },\n  \"rulesDirectory\": []\n}\n"
  }
]