diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..306951e22 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +**/ci-install.sh diff --git a/.eslintrc b/.eslintrc index faa56f0c3..4b6fba213 100644 --- a/.eslintrc +++ b/.eslintrc @@ -229,7 +229,7 @@ "object-curly-newline": "off", "object-curly-spacing": "off", "object-property-newline": "off", - "one-var": ["error", "always"], + "one-var": ["error", "consecutive"], "one-var-declaration-per-line": "error", "operator-assignment": "error", "operator-linebreak": ["error", "after"], diff --git a/.github/workflows/draft-new-release.yml b/.github/workflows/draft-new-release.yml new file mode 100644 index 000000000..1aa067a82 --- /dev/null +++ b/.github/workflows/draft-new-release.yml @@ -0,0 +1,78 @@ +name: Draft new release + +on: + workflow_dispatch: + inputs: + version: + description: The version you want to release. Must be a valid semver version. + required: true + type: string + +jobs: + draft-new-release: + if: startsWith(github.event.inputs.version, 'v') + name: Draft a new release + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Create release branch + run: git checkout -b release/${{ github.event.inputs.version }} + + - name: Update changelog + uses: thomaseizinger/keep-a-changelog-new-release@1.1.0 + with: + version: ${{ github.event.inputs.version }} + + - name: Initialize mandatory git config + run: | + git config user.name "GitHub Actions" + git config user.email noreply@github.com + + - name: Bump version + run: npm version ${{ github.event.inputs.version }} --git-tag-version false + + - name: Commit changelog and manifest files + id: make-commit + run: | + git add CHANGELOG.md package.json package-lock.json + git commit --message "Prepare release ${{ github.event.inputs.version }}" + echo "::set-output name=commit::$(git rev-parse HEAD)" + + - name: Push new branch + run: git push origin release/${{ github.event.inputs.version }} + + - name: Create pull request for master + uses: thomaseizinger/create-pull-request@1.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + head: release/${{ github.event.inputs.version }} + base: master + title: "Release version ${{ github.event.inputs.version }}" + reviewers: ${{ github.actor }} + body: | + Hi @${{ github.actor }}! + + This PR was created in response to a manual trigger of the release workflow here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}. + I've updated the changelog and bumped the versions in the manifest files in this commit: ${{ steps.make-commit.outputs.commit }}. + + - name: Create pull request for develop + uses: thomaseizinger/create-pull-request@1.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + head: release/${{ github.event.inputs.version }} + base: develop + title: "Release version ${{ github.event.inputs.version }}" + reviewers: ${{ github.actor }} + body: | + Hi @${{ github.actor }}! + + This PR was created in response to a manual trigger of the release workflow here: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}. + I've updated the changelog and bumped the versions in the manifest files in this commit: ${{ steps.make-commit.outputs.commit }}. diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yml new file mode 100644 index 000000000..7983264d7 --- /dev/null +++ b/.github/workflows/publish-new-release.yml @@ -0,0 +1,46 @@ +name: "Publish new release" + +on: + pull_request: + branches: + - master + types: + - closed + +jobs: + release: + name: Publish new release + runs-on: ubuntu-latest + # only merged pull requests that begin with 'release/' or 'hotfix/' must trigger this job + if: github.event.pull_request.merged == true && + (contains(github.event.pull_request.head.ref, 'release/') || contains(github.event.pull_request.head.ref, 'hotfix/')) + permissions: + contents: write + + steps: + - name: Extract version from branch name (for release branches) + if: contains(github.event.pull_request.head.ref, 'release/') + run: | + BRANCH_NAME="${{ github.event.pull_request.head.ref }}" + VERSION=${BRANCH_NAME#release/} + + echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV + + - name: Extract version from branch name (for hotfix branches) + if: contains(github.event.pull_request.head.ref, 'hotfix/') + run: | + BRANCH_NAME="${{ github.event.pull_request.head.ref }}" + VERSION=${BRANCH_NAME#hotfix/} + + echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV + + - name: Create Release + uses: thomaseizinger/create-release@1.0.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + target_commitish: ${{ github.event.pull_request.merge_commit_sha }} + tag_name: ${{ env.RELEASE_VERSION }} + name: ${{ env.RELEASE_VERSION }} + draft: false + prerelease: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..8f3a28682 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: Test + +on: + push: + branches: + - develop + - master + pull_request: + +jobs: + Unit-Tests: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + max-parallel: 10 + matrix: + node-version: [ 20.x ] + language-variant: [ 'csharp-httpclient', 'csharp-restsharp', 'curl', 'dart-dio', 'dart-http', 'golang', 'http', + 'java-okhttp', 'java-unirest', 'js-fetch', 'js-jquery', 'js-xhr', 'kotlin-okhttp', 'libcurl', + 'nodejs-axios', 'nodejs-native', 'nodejs-request', 'nodejs-unirest', 'objective-c', 'ocaml-cohttp', + 'php-curl', 'php-guzzle', 'php-httprequest2', 'php-pecl-http', 'powershell-restmethod', + 'python-http.client', 'python-requests', 'r-httr', 'r-rcurl', 'ruby', 'rust-reqwest', + 'shell-httpie', 'shell-wget', 'swift' ] + steps: + - name: Get Code + uses: actions/checkout@v3 + - name: Use Node JS ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: Install package dependencies + run: npm install + - name: Install system dependencies + run: npm run cirequirements ${{ matrix.language-variant }} + - name: Install dependencies for individual codegens + run: npm run deepinstall dev + - name: Run tests + run: npm run test ${{ matrix.language-variant }} diff --git a/.gitignore b/.gitignore index 5a57e17a9..2f636f5aa 100644 --- a/.gitignore +++ b/.gitignore @@ -64,8 +64,11 @@ typings/ # next.js build output .next -out/ +# Cargo +target/ +# Newman test generated files +out/ newmanResponses.json dummyFile*.txt -dummyBinaryFile +dummyBinaryFile \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4e95bf5d4..000000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -dist: 'xenial' -language: node_js -node_js: - - '8' -before_install: - - echo "Installing additional dependencies required for running tests on travis" - - npm run cirequirements -before_script: - - echo "Installing dev dependencies required of all codegens" - - npm run deepinstall dev \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ac89cf150..77f36a77c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,55 +1,239 @@ +# Postman Code Generators Changelog + +## [Unreleased] + +## [v2.1.0] - 2025-11-12 + +### Feature +- Support Postman CLI's request command. + +### Chore +- Upgraded build pipeline to use Ubuntu 22 (in response to GitHub Action's deprecation.) + +## [v2.0.0] - 2025-03-10 + +### Breaking Changes + +- Dropped support for node < v18. + +## [v1.14.2] - 2025-02-21 + +### Fixed + +- Fix for - [#772](https://github.com/postmanlabs/postman-code-generators/issues/772) Ignore workspace dependencies while deepinstall for pnpm. + +## [v1.14.1] - 2024-12-18 + +### Fixed + +- Fix for - [#780](https://github.com/postmanlabs/postman-code-generators/issues/780) Made the codegens package manager agnostic by removing lock files. + +## [v1.14.0] - 2024-10-10 + +### Fixed + +- Fix for - [#5330](https://github.com/postmanlabs/postman-app-support/issues/5330) Added support for usage of --data-binary flag when using long format option for body type binary. + +- Fix for - [#540](https://github.com/postmanlabs/postman-code-generators/issues/540) Curl codesnippet's JSON body must follow multiLine option's configuration. + +## [v1.13.0] - 2024-09-11 + +### Fixed + +- Fix for - [#760](https://github.com/postmanlabs/postman-code-generators/issues/760) Fixed package installation issues with yarn (v4) and pnpm. + +## [v1.12.0] - 2024-07-22 + +### Chore + +- Updated postman-collection sdk to version 4.4.0 in missing codegens. + +### Fixed + +- Fix typo in Content-Header for audio/midi files in codegens. +- Added support for NTLM auth support in cURL codegen. + +## [v1.11.0] - 2024-07-10 + +### Chore + +- Updated postman-collection to v4.4.0. + +## [v1.10.1] - 2024-05-06 + +### Fixed + +- Fix `+` char being encoded in query params of multiple languages. +- Fix special characters not being escaped in multiple languages. + +## [v1.9.0] - 2024-01-18 + +### Fixed + +- Fix for - [#10139](https://github.com/postmanlabs/postman-app-support/issues/10139) Modify Swift codegen to work with multipart/form-data format, used for video file upload + +## [v1.8.0] - 2023-06-27 + +- Fix for - [#10521](https://github.com/postmanlabs/postman-app-support/issues/10521) Add support for Dart Dio snippet generation + +## [v1.7.2] - 2023-05-04 + +### Fixed + +- Fix for - [#11934](https://github.com/postmanlabs/postman-app-support/issues/11934) Prevent `%` double encoding in query params + +## [v1.7.1] - 2023-03-29 + +- Minor fix - Add language labels for Rust and Kotlin + +## Previous Releases + +Newer releases follow the [Keep a Changelog](https://keepachangelog.com) format. + +v1.7.0 (March 28, 2023) + +- Fix for - [#192](https://github.com/postmanlabs/postman-code-generators/issues/192) Added support for Rust reqwest code snippets. + +v1.6.1 (March 27, 2023) + +- Fix backlashes being unescaped unnecessarily in cURL codegen + +v1.6.0 (March 17, 2023) + +- PEP8 improvements in python-requests code +- Fix for - [#491](https://github.com/postmanlabs/postman-code-generators/issues/491) Added support for kotlin okhttp code snippets. +- Refactored code for nodejs-axios util.js. + +v1.5.1 (March 28, 2023) + +- Fix backlashes being escaped unnecessarily in cURL codegen + +v1.5.0 (March 2, 2023) + +- Change minimum supported NodeJS version to 12 +- Fix for - [#11049](https://github.com/postmanlabs/postman-app-support/issues/11049) Escape backslash character in raw bodies for curl codegen +- Fix for - [#302](https://github.com/postmanlabs/postman-code-generators/issues/302) Add option to use async/await in NodeJS Axios codegen +- Fix for - [#322](https://github.com/postmanlabs/postman-code-generators/issues/322) Use multiline quotes in Powershell to simplify generated code +- Add long form option for -g flag in curl codegen +- Minor Swift codegen improvements + +v1.4.1 (February 22, 2023) + +- cURL codegen should work when request has a protocolProfileBehavior with null value + +v1.4.0 (February 6, 2023) + +- Add support for C# HttpClient Codegen +- Fix for - [#9511](https://github.com/postmanlabs/postman-app-support/issues/9511) - Use short options in CURL as long as possible +- Fix for - [#10581](https://github.com/postmanlabs/postman-app-support/issues/10581) - Do not add HTTP method explicitly in CURL when not required +- Fix for - [#10053](https://github.com/postmanlabs/postman-app-support/issues/10053) - Remove usage of semaphore from Swift Codegen + +v1.3.0 (December 16, 2022) + +- Update C# restsharp codegen to support [107](https://restsharp.dev/v107/) +- Fix for - [#11084](https://github.com/postmanlabs/postman-app-support/issues/11084) Fixes an issue where HTTP code snippet was generating wrong boundaries +- Fixes an issue with Axios code snippets not including maxBodyLength param + +v1.2.1 (April 26, 2022) + +- Add label for 'R' language + +v1.2.0 (April 22, 2022) + +- Add new codegens - php-guzzle, R-httr, R-rcurl +- Fix issue with pipeline failing due to updated version of RestSharp +- Fix for - [#502](https://github.com/postmanlabs/postman-code-generators/issues/502) Allow GET method to have a body in java-okhttp if present in input request +- Fix for - [#476](https://github.com/postmanlabs/postman-code-generators/pull/476) Properly escape already escaped double quotes in curl body + v1.1.5 (May 10, 2021) -* Fixed an issue with how JSON bodies are shown in code snippets for Ruby, C#, and Dart. + +- Fixed an issue with how JSON bodies are shown in code snippets for Ruby, C#, and Dart. v1.1.4 (May 6, 2021) -* Fix an issue with empty GraphQL body + +- Fix an issue with empty GraphQL body v1.1.3 (Mar 2, 2021) -* Use proper indentation for JSON bodies in Javascript and Nodejs codegens -* Fix for - [445](https://github.com/postmanlabs/postman-code-generators/issues/445) Add proper indentation in nodejs-axios when bodytype is urlencoded -* Fix for - [248](https://github.com/postmanlabs/postman-code-generators/issues/248) Use quoteType everywhere in curl, not just in the url -* Fix for - [454](https://github.com/postmanlabs/postman-code-generators/issues/454) Fix encoding when generating HTTP code snippets -* Fix for - [426](https://github.com/postmanlabs/postman-code-generators/issues/426) Use json.dumps in Python codegens if Content-Type is JSON + +- Use proper indentation for JSON bodies in Javascript and Nodejs codegens +- Fix for - [#445](https://github.com/postmanlabs/postman-code-generators/issues/445) Add proper indentation in nodejs-axios when bodytype is urlencoded +- Fix for - [#248](https://github.com/postmanlabs/postman-code-generators/issues/248) Use quoteType everywhere in curl, not just in the url +- Fix for - [#454](https://github.com/postmanlabs/postman-code-generators/issues/454) Fix encoding when generating HTTP code snippets +- Fix for - [#426](https://github.com/postmanlabs/postman-code-generators/issues/426) Use json.dumps in Python codegens if Content-Type is JSON v1.1.2 (Dec 15, 2020) -* Fix for - [8736](https://github.com/postmanlabs/postman-app-support/issues/8736) Add content type support for individual form-data fields -* Fix for - [8635](https://github.com/postmanlabs/postman-app-support/issues/8635) Use Json.parse for all json like application types -* Fix for - [9212](https://github.com/postmanlabs/postman-app-support/issues/9212) Add semicolon after header key in curl codegen if the value is empty string. -* Add Newman test for powershell + +- Fix for - [#8736](https://github.com/postmanlabs/postman-app-support/issues/8736) Add content type support for individual form-data fields +- Fix for - [#8635](https://github.com/postmanlabs/postman-app-support/issues/8635) Use Json.parse for all json like application types +- Fix for - [#9212](https://github.com/postmanlabs/postman-app-support/issues/9212) Add semicolon after header key in curl codegen if the value is empty string. +- Add Newman test for powershell v1.1.1 (Nov 10, 2020) -* Change string to enum in cURL quoteType option. -* Fix new line issue in dart-http and HTTP codegen -* Fix an issue where deepinstall was failing when folder name had spaces. + +- Change string to enum in cURL quoteType option. +- Fix new line issue in dart-http and HTTP codegen +- Fix an issue where deepinstall was failing when folder name had spaces. v1.1.0 (Nov 2, 2020) -* Added support for Dart http -* Fix for - [315](https://github.com/postmanlabs/postman-code-generators/issues/315): Manually parse url provided in the request. -* Fix for - [253](https://github.com/postmanlabs/postman-code-generators/issues/253): Add -g flag to curl if braces ({}) or brackets ([]) are present in the url. -* Fix for - [257](https://github.com/postmanlabs/postman-code-generators/issues/257): Use double quotes to escape semicolon in curl requests -* Fix for - [247](https://github.com/postmanlabs/postman-code-generators/issues/247): Add ContentType to python snippets for multipart/formdata -* Fix for - [186](https://github.com/postmanlabs/postman-code-generators/issues/186): Add ` as line continuation delimiter for curl codegen -* Fix for - [248](https://github.com/postmanlabs/postman-code-generators/issues/248): Add quoteType as an additional option in curl codegen -* Fix deadlock in error case in Swift and Objective-C codegens. -* Fix for - [325](https://github.com/postmanlabs/postman-code-generators/issues/325): Use encodeURIComponent instead of escape for urlencoded request body. -* Fix for - [350](https://github.com/postmanlabs/postman-code-generators/issues/350): Sanitize \r in request body. -* Fix for - [366](https://github.com/postmanlabs/postman-code-generators/issues/366): Add support for uploading binary files for multipart/form-data bodies in python-http.client. -* Fix for - [353](https://github.com/postmanlabs/postman-code-generators/issues/353): Add optional import of FoundationNetworking in swift codegen -* Fix for - [284](https://github.com/postmanlabs/postman-code-generators/issues/284): Replace double-quotes by single-quotes in codegen/php-curl -* Fix for - [330](https://github.com/postmanlabs/postman-code-generators/issues/330): Use url.toString method for converting url in shell-httpie codegen + +- Added support for Dart http +- Fix for - [#315](https://github.com/postmanlabs/postman-code-generators/issues/315): Manually parse url provided in the request. +- Fix for - [#253](https://github.com/postmanlabs/postman-code-generators/issues/253): Add -g flag to curl if braces ({}) or brackets ([#]) are present in the url. +- Fix for - [#257](https://github.com/postmanlabs/postman-code-generators/issues/257): Use double quotes to escape semicolon in curl requests +- Fix for - [#247](https://github.com/postmanlabs/postman-code-generators/issues/247): Add ContentType to python snippets for multipart/formdata +- Fix for - [#186](https://github.com/postmanlabs/postman-code-generators/issues/186): Add \` as line continuation delimiter for curl codegen +- Fix for - [#248](https://github.com/postmanlabs/postman-code-generators/issues/248): Add quoteType as an additional option in curl codegen +- Fix deadlock in error case in Swift and Objective-C codegens. +- Fix for - [#325](https://github.com/postmanlabs/postman-code-generators/issues/325): Use encodeURIComponent instead of escape for urlencoded request body. +- Fix for - [#350](https://github.com/postmanlabs/postman-code-generators/issues/350): Sanitize \\r in request body. +- Fix for - [#366](https://github.com/postmanlabs/postman-code-generators/issues/366): Add support for uploading binary files for multipart/form-data bodies in python-http.client. +- Fix for - [#353](https://github.com/postmanlabs/postman-code-generators/issues/353): Add optional import of FoundationNetworking in swift codegen +- Fix for - [#284](https://github.com/postmanlabs/postman-code-generators/issues/284): Replace double-quotes by single-quotes in codegen/php-curl +- Fix for - [#330](https://github.com/postmanlabs/postman-code-generators/issues/330): Use url.toString method for converting url in shell-httpie codegen v1.0.2 (Oct 15, 2020) -* Fixed spaces around variables and arguments in Python codgen to comply with PEP 8. -* Added Content-Length header to generated HTTP snippets. -* Switched to multiline strings for Raw bodies in Go. -* Stopped manually encoding response bodes in `utf8` for Python-requests. -* Removed unnecessary semicolons at the end of statements in Ruby. -* Fixed wrong name of HTTP codegen in README + +- Fixed spaces around variables and arguments in Python codgen to comply with PEP 8. +- Added Content-Length header to generated HTTP snippets. +- Switched to multiline strings for Raw bodies in Go. +- Stopped manually encoding response bodes in `utf8` for Python-requests. +- Removed unnecessary semicolons at the end of statements in Ruby. +- Fixed wrong name of HTTP codegen in README v1.0.1 (Jun 29, 2020) -- Fix for - [8674](https://github.com/postmanlabs/postman-app-support/issues/8674): Add URL sanitization for quotes in cURL, Java Unirest, NodeJS Native, Python http.client, and Swift. + +- Fix for - [#8674](https://github.com/postmanlabs/postman-app-support/issues/8674): Add URL sanitization for quotes in cURL, Java Unirest, NodeJS Native, Python http.client, and Swift. v1.0.0 (May 29, 2020) -- Add axios framework support -- Add ES6 syntax support for NodeJS Request, NodeJS Native and NodeJS Unirest -- Fix snippet generation for powershell and jquery, where form data params had no type field \ No newline at end of file + +- Add axios framework support +- Add ES6 syntax support for NodeJS Request, NodeJS Native and NodeJS Unirest +- Fix snippet generation for powershell and jquery, where form data params had no type field + +[Unreleased]: https://github.com/postmanlabs/postman-code-generators/compare/v2.1.0...HEAD + +[v2.1.0]: https://github.com/postmanlabs/postman-code-generators/compare/v2.0.0...v2.1.0 + +[v2.0.0]: https://github.com/postmanlabs/postman-code-generators/compare/v1.14.2...v2.0.0 + +[v1.14.2]: https://github.com/postmanlabs/postman-code-generators/compare/v1.14.1...v1.14.2 + +[v1.14.1]: https://github.com/postmanlabs/postman-code-generators/compare/v1.14.0...v1.14.1 + +[v1.14.0]: https://github.com/postmanlabs/postman-code-generators/compare/v1.13.0...v1.14.0 + +[v1.13.0]: https://github.com/postmanlabs/postman-code-generators/compare/v1.12.0...v1.13.0 + +[v1.12.0]: https://github.com/postmanlabs/postman-code-generators/compare/v1.11.0...v1.12.0 + +[v1.11.0]: https://github.com/postmanlabs/postman-code-generators/compare/v1.10.1...v1.11.0 + +[v1.10.1]: https://github.com/postmanlabs/postman-code-generators/compare/v1.10.0...v1.9.0 + +[v1.9.0]: https://github.com/postmanlabs/postman-code-generators/compare/v1.8.0...v1.9.0 + +[v1.8.0]: https://github.com/postmanlabs/postman-code-generators/compare/v1.7.2...v1.8.0 + +[v1.7.2]: https://github.com/postmanlabs/postman-code-generators/compare/v1.7.1...v1.7.2 + +[v1.7.1]: https://github.com/postmanlabs/postman-code-generators/compare/v1.7.0...v1.7.1 diff --git a/README.md b/README.md index ac32c2ea3..18684d8c0 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ _Manage all of your organization's APIs in Postman, with the industry's most com *Supercharge your API workflow.* *Modern software is built on APIs. Postman helps you develop APIs faster.* -# postman-code-generators [![Build Status](https://travis-ci.com/postmanlabs/postman-code-generators.svg?branch=master)](https://travis-ci.com/postmanlabs/postman-code-generators) +# postman-code-generators -This module converts a [Postman SDK](https://github.com/postmanlabs/postman-collection) Request Object into a code snippet of chosen language. +This module converts a [Postman SDK](https://github.com/postmanlabs/postman-collection) Request [Object](https://www.postmanlabs.com/postman-collection/Request.html) into a code snippet of chosen language. Every code generator has two identifiers: `language` and `variant`. * `language` of a code generator is the programming language in which the code snippet is generated. @@ -18,6 +18,7 @@ List of supported code generators: | Language | Variant | |-----------|---------------| | C | libcurl | +| C# | HttpClient | | C# | RestSharp | | cURL | cURL | | Dart | http | @@ -28,34 +29,47 @@ List of supported code generators: | JavaScript | Fetch | | JavaScript | jQuery | | JavaScript | XHR | +| Kotlin | OkHttp | | NodeJs | Axios | | NodeJs | Native | | NodeJs | Request | | NodeJs | Unirest | | Objective-C| NSURLSession| | OCaml | Cohttp | -|PHP | cURL | -|PHP | pecl_http | -|PHP | HTTP_Request2 | +| PHP | cURL | +| PHP | Guzzle | +| PHP | pecl_http | +| PHP | HTTP_Request2 | | PowerShell | RestMethod | | Python | http.client | | Python | Requests | +| R | httr | +| R | RCurl | +| Rust | Reqwest | | Ruby | Net:HTTP | | Shell | Httpie | | Shell | wget | | Swift | URLSession | ## Table of contents -1. [Getting Started](#getting-started) -2. [Prerequisite](#prerequisite) -3. [Usage](#usage) - 1. [Using postman code generators as a Library](#using-postman-code-generators-as-a-library) -4. [Development](#development) - 1. [Installing Dependencies](#installing-dependencies) - 2. [Testing](#testing) - 3. [Packaging](#packaging) -7. [Contributing](#contributing) -8. [License](#license) +- [postman-code-generators + - [Table of contents](#table-of-contents) + - [Getting Started](#getting-started) + - [Prerequisite](#prerequisite) + - [Usage](#usage) + - [Using postman-code-generators as a Library](#using-postman-code-generators-as-a-library) + - [getLanguageList](#getlanguagelist) + - [Example:](#example) + - [getOptions](#getoptions) + - [Example:](#example-1) + - [convert](#convert) + - [Example:](#example-2) + - [Development](#development) + - [Installing dependencies](#installing-dependencies) + - [Testing](#testing) + - [Packaging](#packaging) + - [Contributing](#contributing) + - [License](#license) ## Getting Started To install postman-code-generators as your dependency @@ -157,7 +171,7 @@ var codegen = require('postman-code-generators'), // require postman-code-genera This function takes in five parameters and returns a callback with error and generated code snippet * `language` - lang key from the language list returned from getLanguageList function * `variant` - variant key provided by getLanguageList function -* `request` - [Postman-SDK](https://github.com/postmanlabs/postman-collection) Request Object +* `request` - [Postman-SDK](https://github.com/postmanlabs/postman-collection) Request [Object](https://www.postmanlabs.com/postman-collection/Request.html) * `options` - Options that can be used to configure generated code snippet. Defaults will be used for the unspecified attributes * `callback` - callback function with first parameter as error and second parameter as string for code snippet diff --git a/codegens/csharp-httpclient/.gitignore b/codegens/csharp-httpclient/.gitignore new file mode 100644 index 000000000..8f6896c8e --- /dev/null +++ b/codegens/csharp-httpclient/.gitignore @@ -0,0 +1,50 @@ +.DS_Store +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + +# Coverage directory used by tools like istanbul +.coverage + +# node-waf configuration +.lock-wscript + + +# Dependency directories +node_modules/ +jspm_packages/ + +# Test Project directories +testProject/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ diff --git a/codegens/csharp-httpclient/.jsdoc-config.json b/codegens/csharp-httpclient/.jsdoc-config.json new file mode 100644 index 000000000..a61209002 --- /dev/null +++ b/codegens/csharp-httpclient/.jsdoc-config.json @@ -0,0 +1,35 @@ +{ + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc", "closure"] + }, + "source": { + "include": ["lib"], + "includePattern": ".+\\.js(doc)?$", + "excludePattern": "(^|\\/|\\\\)_" + }, + + "plugins": [ + "plugins/markdown" + ], + + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "highlightTutorialCode" : true + }, + + "opts": { + "template": "./node_modules/postman-jsdoc-theme", + "encoding": "utf8", + "destination": "./out/docs", + "recurse": true, + "readme": "README.md" + }, + + "markdown": { + "parser": "gfm", + "hardwrap": false + } + } + \ No newline at end of file diff --git a/codegens/csharp-httpclient/.npmignore b/codegens/csharp-httpclient/.npmignore new file mode 100644 index 000000000..79ad2ba5f --- /dev/null +++ b/codegens/csharp-httpclient/.npmignore @@ -0,0 +1,76 @@ +### NPM Specific: Disregard recursive project files +### =============================================== +/.editorconfig +/.gitmodules +/test + +### Borrowed from .gitignore +### ======================== + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +snippet.swift + +out/ diff --git a/codegens/csharp-httpclient/README.md b/codegens/csharp-httpclient/README.md new file mode 100644 index 000000000..a6b89e9ff --- /dev/null +++ b/codegens/csharp-httpclient/README.md @@ -0,0 +1,42 @@ + +> Converts Postman-SDK Request into code snippet for . + +#### Prerequisites +To run Code-Gen, ensure that you have NodeJS >= v8. A copy of the NodeJS installable can be downloaded from https://nodejs.org/en/download/package-manager. + +## Using the Module +The module will expose an object which will have property `convert` which is the function for converting the Postman-SDK request to swift code snippet. + +### convert function +Convert function takes three parameters + +* `request` - Postman-SDK Request Object + +* `options` - options is an object which hsa following properties + * `indentType` - String denoting type of indentation for code snippet. eg: 'Space', 'Tab' + * `indentCount` - The number of indentation characters to add per code level + * `trimRequestBody` - Whether or not request body fields should be trimmed + +* `callback` - callback function with first parameter as error and second parameter as string for code snippet + +##### Example: +```js +var request = new sdk.Request('www.google.com'), //using postman sdk to create request + options = { + indentCount: 3, + indentType: 'Space', + requestTimeout: 200, + trimRequestBody: true + }; +convert(request, options, function(error, snippet) { + if (error) { + // handle error + } + // handle snippet +}); +``` +### Guidelines for using generated snippet + +* Since Postman-SDK Request object doesn't provide complete path of the file, it needs to be manually inserted in case of uploading a file. + +* This module doesn't support cookies. \ No newline at end of file diff --git a/codegens/csharp-httpclient/index.js b/codegens/csharp-httpclient/index.js new file mode 100644 index 000000000..bb0a047c4 --- /dev/null +++ b/codegens/csharp-httpclient/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/codegens/csharp-httpclient/lib/CodeBuilder.js b/codegens/csharp-httpclient/lib/CodeBuilder.js new file mode 100644 index 000000000..2611b077d --- /dev/null +++ b/codegens/csharp-httpclient/lib/CodeBuilder.js @@ -0,0 +1,114 @@ +/** + * + */ +class CodeBuilder { + /** + * + * @param {Number} indentSize + * @param {String} indentCharacter + */ + constructor (indentSize, indentCharacter) { + this.indentSize = indentSize; + this.indentCharacter = indentCharacter; + this.currentIndentCount = 0; + this.snippet = ''; + this.usings = []; + this.newLineChar = '\n'; + } + + /** + * + * @param {String} using - The namespace that is needed to be imported in order + * for the code to be able to be built on it's own + */ + addUsing (using) { + this.usings.push(using); + } + + /** + * + * @param {String} line - the line to be appended + */ + appendLine (line) { + this.snippet += this.indentation + line + this.newLineChar; + } + + /** + * + * @param {String[]} lines + */ + appendLines (lines) { + lines.forEach((l) => { + this.appendLine(l); + }); + } + + /** + * + * @param {String} body - the value to append to the running block of code + */ + append (body) { + this.snippet += body; + } + + /** + * + * @param {String} line - the line to be appened followed by the start of a new block + */ + appendBlock (line) { + this.snippet += this.indentation + line + this.newLineChar + + this.indentation + '{' + this.newLineChar; + this.currentIndentCount++; + } + + /** + * + * @param {String} extra - value to add after closing brace + */ + endBlock (extra) { + if (!extra) { + extra = ''; + } + this.currentIndentCount--; + this.snippet += this.indentation + '}' + extra + this.newLineChar; + } + + /** + * + * @param {Boolean} addUsings - Whether to actually append the usings at the top + * + * @returns {String} the full block of code + */ + build (addUsings) { + if (addUsings) { + var builder = new CodeBuilder(this.indentSize, this.indentCharacter); + this.uniqueUsings().forEach((using) => { + builder.appendLine(`using ${using};`); + }); + builder.append(this.snippet); + return builder.build(false); + } + + return this.snippet; + } + + /** + * @returns {Array} the unique usings + */ + uniqueUsings () { + var arr = [], + i = 0; + for (i = 0; i < this.usings.length; i++) { + if (!arr.includes(this.usings[i])) { + arr.push(this.usings[i]); + } + } + return arr.sort(); + } + + get indentation () { + return this.indentCharacter.repeat(this.indentSize * this.currentIndentCount); + } +} + +module.exports = CodeBuilder; diff --git a/codegens/csharp-httpclient/lib/httpclient.js b/codegens/csharp-httpclient/lib/httpclient.js new file mode 100644 index 000000000..7bec051e6 --- /dev/null +++ b/codegens/csharp-httpclient/lib/httpclient.js @@ -0,0 +1,159 @@ + +var _ = require('./lodash'), + parseRequest = require('./parseRequest'), + sanitize = require('./util').sanitize, + csharpify = require('./util').csharpify, + sanitizeOptions = require('./util').sanitizeOptions, + CodeBuilder = require('./CodeBuilder'), + self; + +/** + * + * @param {CodeBuilder} builder - Code builder for generating code + * @param {Object} request - Postman SDK request object + * @param {Object} options - Options to tweak code snippet generated in C# + * @param {String} options.indentType - type for indentation eg: Space, Tab (default: Space) + * @param {String} options.indentCount - number of spaces or tabs for indentation. (default: 4 for indentType: + * Space, default: 1 for indentType: Tab) + * @param {Boolean} [options.includeBoilerplate] - indicates whether to include class definition in C# + * @param {Number} options.requestTimeout - time in seconds after which request will bail out + * (default: 0 -> use .NET default timeout of 100 seconds) + * @param {Boolean} options.followRedirect - whether to enable follow redirect + * @returns {String} csharp-httpclient code snippet for given request object + */ +function makeSnippet (builder, request, options) { + const IS_PROPERTY_METHOD = [ 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE' ]; + + if (options.followRedirect) { + // By default .NET does follow redirects so we can just leave this alone + builder.appendLine('var client = new HttpClient();'); + } + else { + builder.appendBlock('var client = new HttpClient(new HttpClientHandler'); + builder.appendLine('AllowAutoRedirect = false,'); + builder.endBlock(');'); + } + + if (options.requestTimeout !== 0) { + builder.appendLine(`client.Timeout = TimeSpan.FromSeconds(${options.requestTimeout});`); + } + + // Create the request + builder.append(`${builder.indentation}var request = new HttpRequestMessage(`); + + if (IS_PROPERTY_METHOD.includes(request.method)) { + builder.append(`HttpMethod.${csharpify(request.method)}`); + } + else { + builder.append(`new HttpMethod("${request.method}")`); + } + + builder.append(`, "${sanitize(request.url.toString())}");${builder.newLineChar}`); + // Finish the initial creation of the request + + // Parse headers + parseRequest.parseHeader(builder, request.toJSON()); + + // Configure the body + parseRequest.parseBody(builder, request); + builder.appendLine('var response = await client.SendAsync(request);'); + builder.appendLine('response.EnsureSuccessStatusCode();'); + builder.appendLine('Console.WriteLine(await response.Content.ReadAsStringAsync());'); +} + +self = module.exports = { + + /** + * Used in order to get additional options for generation of C# code snippet (i.e. Include Boilerplate code) + * + * @module getOptions + * + * @returns {Array} Additional options specific to generation of csharp-httpclient code snippet + */ + getOptions: function () { + return [ + { + name: 'Include boilerplate', + id: 'includeBoilerplate', + type: 'boolean', + default: false, + description: 'Include class definition and import statements in snippet' + }, + { + name: 'Set indentation count', + id: 'indentCount', + type: 'positiveInteger', + default: 2, + description: 'Set the number of indentation characters to add per code level' + }, + { + name: 'Set indentation type', + id: 'indentType', + type: 'enum', + availableOptions: ['Tab', 'Space'], + default: 'Space', + description: 'Select the character used to indent lines of code' + }, + { + name: 'Set request timeout', + id: 'requestTimeout', + type: 'positiveInteger', + default: 0, + description: 'Set number of milliseconds the request should wait for a response before timing out ' + + '(use 0 for infinity)' + }, + { + name: 'Follow redirects', + id: 'followRedirect', + type: 'boolean', + description: 'Automatically follow HTTP redirects', + default: true + } + ]; + }, + + /** + * Converts Postman sdk request object to csharp-httpclient code snippet + * + * @module convert + * + * @param {Object} request - Postman-SDK request object + * @param {Object} options - Options to tweak code snippet generated in C# + * @param {String} options.indentType - type for indentation eg: Space, Tab (default: Space) + * @param {String} options.indentCount - number of spaces or tabs for indentation. (default: 4 for indentType: + * Space, default: 1 for indentType: Tab) + * @param {Boolean} [options.includeBoilerplate] - indicates whether to include class definition in C# + * @param {Number} options.requestTimeout - time in seconds after which request will bail out + * (default: 0 -> use .NET default timeout of 100 seconds) + * @param {Boolean} options.followRedirect - whether to enable follow redirect + * @param {Function} callback - Callback function with parameters (error, snippet) + * + * @returns {String} Generated C# snippet via callback + */ + convert: function (request, options, callback) { + if (!_.isFunction(callback)) { + throw new Error('C#-HttpClient-Converter: Callback is not valid function'); + } + + // String representing value of indentation required + var indentString, + codeBuilder; + + // TODO: Sanitize options here + options = sanitizeOptions(options, self.getOptions()); + + // TODO: Get this stuff from options + indentString = options.indentType === 'Tab' ? '\t' : ' '; + + codeBuilder = new CodeBuilder(options.indentCount, indentString); + + if (options.includeBoilerplate) { + codeBuilder.appendLine('// No more boilerplate needed with top level statements ' + + '(https://docs.microsoft.com/en-us/dotnet/core/tutorials/top-level-templates)'); + } + + makeSnippet(codeBuilder, request, options); + + return callback(null, codeBuilder.build(options.includeBoilerplate)); + } +}; diff --git a/codegens/csharp-httpclient/lib/index.js b/codegens/csharp-httpclient/lib/index.js new file mode 100644 index 000000000..d2982b3b1 --- /dev/null +++ b/codegens/csharp-httpclient/lib/index.js @@ -0,0 +1,4 @@ +module.exports = { + convert: require('./httpclient').convert, + getOptions: require('./httpclient').getOptions +}; diff --git a/codegens/csharp-httpclient/lib/lodash.js b/codegens/csharp-httpclient/lib/lodash.js new file mode 100644 index 000000000..5be147afd --- /dev/null +++ b/codegens/csharp-httpclient/lib/lodash.js @@ -0,0 +1,455 @@ +/* istanbul ignore next */ +module.exports = { + + /** + * Checks if `value` is an empty object, array or string. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Values such as strings, arrays are considered empty if they have a `length` of `0`. + * + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * isEmpty(null) + * // => true + * + * isEmpty(true) + * // => true + * + * isEmpty(1) + * // => true + * + * isEmpty([1, 2, 3]) + * // => false + * + * isEmpty('abc') + * // => false + * + * isEmpty({ 'a': 1 }) + * // => false + */ + isEmpty: function (value) { + // eslint-disable-next-line lodash/prefer-is-nil + if (value === null || value === undefined) { + return true; + } + if (Array.isArray(value) || typeof value === 'string' || typeof value.splice === 'function') { + return !value.length; + } + + for (const key in value) { + if (Object.prototype.hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + }, + + /** + * Checks if `value` is `undefined`. + * + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * isUndefined(void 0) + * // => true + * + * isUndefined(null) + * // => false + */ + isUndefined: function (value) { + return value === undefined; + }, + + /** + * Checks if `func` is classified as a `Function` object. + * + * @param {*} func The value to check. + * @returns {boolean} Returns `true` if `func` is a function, else `false`. + * @example + * + * isFunction(self.isEmpty) + * // => true + * + * isFunction(/abc/) + * // => false + */ + isFunction: function (func) { + return typeof func === 'function'; + }, + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * capitalize('FRED') + * // => 'Fred' + * + * capitalize('john') + * // => 'John' + */ + + capitalize: function (string) { + return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + }, + + /** + * Reduces `array` to a value which is the accumulated result of running + * each element in `array` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `array` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, array). + * + * @param {Array} array The Array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @example + * + * reduce([1, 2], (sum, n) => sum + n, 0) + * // => 3 + * + */ + reduce: function (array, iteratee, accumulator) { + return array.reduce(iteratee, accumulator); + }, + + /** + * Iterates over elements of `array`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function|object} predicate The function/object invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * filter(users, ({ active }) => active) + * // => object for ['barney'] + */ + filter: function (array, predicate) { + if (typeof predicate === 'function') { + return array.filter(predicate); + } + var key = Object.keys(predicate), + val = predicate[key], + res = []; + array.forEach(function (item) { + if (item[key] && item[key] === val) { + res.push(item); + } + }); + return res; + }, + + /** + * The opposite of `filter` this method returns the elements of `array` + * that `predicate` does **not** return truthy for. + * + * @param {Array} array collection to iterate over. + * @param {String} predicate The String that needs to have truthy value, invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * reject(users, 'active') + * // => object for ['fred'] + */ + reject: function (array, predicate) { + var res = []; + array.forEach((object) => { + if (!object[predicate]) { + res.push(object); + } + }); + return res; + }, + + /** + * Creates an array of values by running each element of `array` thru `iteratee`. + * The iteratee is invoked with three arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n + * } + * + * map([4, 8], square) + * // => [16, 64] + */ + map: function (array, iteratee) { + return array.map(iteratee); + }, + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @example + * + * forEach([1, 2], value => console.log(value)) + * // => Logs `1` then `2`. + * + * forEach({ 'a': 1, 'b': 2 }, (value, key) => console.log(key)) + * // => Logs 'a' then 'b' + */ + + forEach: function (collection, iteratee) { + if (collection === null) { + return null; + } + + if (Array.isArray(collection)) { + return collection.forEach(iteratee); + } + const iterable = Object(collection), + props = Object.keys(collection); + var index = -1, + key, i; + + for (i = 0; i < props.length; i++) { + key = props[++index]; + iteratee(iterable[key], key, iterable); + } + return collection; + }, + + /** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise it checks if the `value` is present + * as a key in a `collection` object. + * + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ + includes: function (collection, value) { + if (Array.isArray(collection) || typeof collection === 'string') { + return collection.includes(value); + } + for (var key in collection) { + if (collection.hasOwnProperty(key)) { + if (collection[key] === value) { + return true; + } + } + } + return false; + }, + + /** + * Gets the size of `collection` by returning its length for array and strings. + * For objects it returns the number of enumerable string keyed + * properties. + * + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * size([1, 2, 3]) + * // => 3 + * + * size({ 'a': 1, 'b': 2 }) + * // => 2 + * + * size('pebbles') + * // => 7 + */ + size: function (collection) { + // eslint-disable-next-line lodash/prefer-is-nil + if (collection === null || collection === undefined) { + return 0; + } + if (Array.isArray(collection) || typeof collection === 'string') { + return collection.length; + } + + return Object.keys(collection).length; + }, + + /** + * Converts all elements in `array` into a string separated by `separator`. + * + * @param {Array} array The array to convert. + * @param {string} [separator=','] The element separator. + * @returns {string} Returns the joined string. + * @example + * + * _.join(['a', 'b', 'c'], '~'); + * // => 'a~b~c' + */ + join: function (array, separator) { + if (array === null) { + return ''; + } + return array.join(separator); + }, + + /** + * Removes trailing whitespace or specified characters from `string`. + * + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + * @example + * + * trimEnd(' abc ') + * // => ' abc' + * + * trimEnd('-_-abc-_-', '_-') + * // => '-_-abc' + */ + trimEnd: function (string, chars) { + if (!string) { + return ''; + } + if (string && !chars) { + return string.replace(/\s*$/, ''); + } + chars += '$'; + return string.replace(new RegExp(chars, 'g'), ''); + }, + + /** + * Returns the index of the first + * element `predicate` returns truthy for. + * + * @param {Array} array The array to inspect. + * @param {Object} predicate The exact object to be searched for in the array. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * _.findIndex(users, {'active' : false}); + * // => 0 + * + */ + findIndex: function (array, predicate) { + var length = array === null ? 0 : array.length, + index = -1, + keys = Object.keys(predicate), + found, i; + if (!length) { + return -1; + } + for (i = 0; i < array.length; i++) { + found = true; + // eslint-disable-next-line no-loop-func + keys.forEach((key) => { + if (!(array[i][key] && array[i][key] === predicate[key])) { + found = false; + } + }); + if (found) { + index = i; + break; + } + } + return index; + }, + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @param {Object} object The object to query. + * @param {string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * const object = { a: {b : 'c'} } + * + * + * get(object, 'a.b.c', 'default') + * // => 'default' + * + * get(object, 'a.b', 'default') + * // => 'c' + */ + get: function (object, path, defaultValue) { + if (object === null) { + return undefined; + } + var arr = path.split('.'), + res = object, + i; + for (i = 0; i < arr.length; i++) { + res = res[arr[i]]; + if (res === undefined) { + return defaultValue; + } + } + return res; + }, + + /** + * Checks if `predicate` returns truthy for **all** elements of `array`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * every([true, 1, null, 'yes'], Boolean) + * // => false + */ + every: function (array, predicate) { + var index = -1, + length = array === null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + +}; diff --git a/codegens/csharp-httpclient/lib/parseRequest.js b/codegens/csharp-httpclient/lib/parseRequest.js new file mode 100644 index 000000000..e2e260db4 --- /dev/null +++ b/codegens/csharp-httpclient/lib/parseRequest.js @@ -0,0 +1,181 @@ +var _ = require('./lodash'), + sanitize = require('./util').sanitize; + +/** + * Returns given content type or default if falsey + * + * @param {String} contentType + * @returns {String} + */ +function parseContentType (contentType) { + return contentType || 'text/plain'; +} + +/** + * Parses header in Postman-SDK request and returns code snippet of csharp-httpclient for adding headers + * + * @param {Object} builder - Code Builder + * @param {Object} requestJson - Postman SDK request object + */ +function parseHeader (builder, requestJson) { + // Possibly add support for the typed header properties + if (!Array.isArray(requestJson.header)) { + return; + } + + requestJson.header.forEach((header) => { + if (!header.disabled && sanitize(header.key) !== 'Content-Type') { + builder.appendLine(`request.Headers.Add("${sanitize(header.key, true)}", "${sanitize(header.value)}");`); + } + }); +} + +/** + * + * @param {CodeBuilder} builder + * @param {Object} requestBody + */ +function parseFormUrlEncoded (builder, requestBody) { + let list = requestBody[requestBody.mode].reduce((collection, data) => { + if (data.disabled) { + return collection; + } + + (!data.value) && (data.value = ''); + collection.push('collection.Add(new' + + `("${sanitize(data.key)}", "${sanitize(data.value)}"));`); + + return collection; + }, []); + + if (list && !_.isEmpty(list)) { + builder.appendLine('var collection = new List>();'); + builder.appendLines(list); + builder.appendLine('var content = new FormUrlEncodedContent(collection);'); + builder.appendLine('request.Content = content;'); + builder.addUsing('System.Collections.Generic'); + } +} + +/** + * + * @param {CodeBuilder} builder + * @param {String} key + * @param {String} fileSrc + */ +function addFile (builder, key, fileSrc) { + builder.appendLine('content.Add(new StreamContent(File.OpenRead' + + `("${sanitize(fileSrc)}")), "${sanitize(key)}", "${sanitize(fileSrc)}");`); +} + +/** + * + * @param {CodeBuilder} builder + * @param {Object} requestBody + */ +function parseFormData (builder, requestBody) { + var allDisabled = requestBody[requestBody.mode].every((i) => { + return i.disabled; + }); + + if (allDisabled) { + return; + } + + builder.appendLine('var content = new MultipartFormDataContent();'); + + requestBody[requestBody.mode].forEach((i) => { + if (i.disabled) { + return; + } + + if (i.type === 'text') { + builder.appendLine('content.Add(new StringContent(' + + `"${sanitize(i.value)}"), "${sanitize(i.key)}");`); + } + else if (i.type === 'file') { + if (Array.isArray(i.src)) { + // Multiple files + i.src.forEach((file) => { + addFile(builder, i.key, file); + }); + } + else { + // Single file + addFile(builder, i.key, i.src); + } + } + }); + + + builder.appendLine('request.Content = content;'); +} + +/** + * + * @param {CodeBuilder} builder + * @param {Object} requestBody + */ +function parseGraphQL (builder, requestBody) { + let query = requestBody.graphql.query, + graphqlVariables; + try { + graphqlVariables = JSON.parse(requestBody.graphql.variables); + } + catch (e) { + graphqlVariables = {}; + } + builder.appendLine('var content = new StringContent(' + + `"${sanitize(JSON.stringify({ query: query, variables: graphqlVariables }))}"` + + ', null, "application/json");'); +} + +/** + * + * @param {CodeBuilder} builder + * @param {Object} request + */ +function parseBody (builder, request) { + var requestBody = request.body ? request.body.toJSON() : {}, + contentType = request.getHeaders({ enabled: true, ignoreCase: true })['content-type']; + if (!_.isEmpty(requestBody)) { + switch (requestBody.mode) { + case 'urlencoded': + parseFormUrlEncoded(builder, requestBody); + break; + case 'formdata': + parseFormData(builder, requestBody); + break; + case 'raw': + builder.appendLine( + `var content = new StringContent(${JSON.stringify(requestBody[requestBody.mode])}, null, ` + + `"${parseContentType(contentType)}");`); + builder.appendLine('request.Content = content;'); + break; + case 'graphql': + parseGraphQL(builder, requestBody); + builder.appendLine('request.Content = content;'); + break; + case 'file': + builder + .appendLine('request.Content = new StreamContent(File.OpenRead("' + + `${sanitize(requestBody[requestBody.mode].src || '""')}"));`); + builder.addUsing('System.IO'); + break; + default: + } + } + else if (contentType) { + // The request has no body but sometimes it wants me to force a content-type anyways + builder.appendLine('var content = new StringContent(string.Empty);'); + builder.appendLine('content.Headers.ContentType = new MediaTypeHeaderValue("' + + `${contentType}");`); + builder.addUsing('System.Net.Http.Headers'); + builder.appendLine('request.Content = content;'); + } +} + +module.exports = { + parseHeader: parseHeader, + parseBody: parseBody +}; diff --git a/codegens/csharp-httpclient/lib/util.js b/codegens/csharp-httpclient/lib/util.js new file mode 100644 index 000000000..3415cdbd3 --- /dev/null +++ b/codegens/csharp-httpclient/lib/util.js @@ -0,0 +1,103 @@ +/** + * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' and trim input if required + * + * @param {String} inputString - Input string to sanitize + * @param {Boolean} [trim] - Indicates whether to trim string or not + * @returns {String} Sanitized String handling escape characters + */ +function sanitize (inputString, trim) { + /* instanbul ignore test */ + if (typeof inputString !== 'string') { + return ''; + } + inputString = inputString.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + return trim ? inputString.trim() : inputString; +} + +/** + * + * @param {String} inputString - The string to return in a C# style + * @returns {String} The string in a C# style + */ +function csharpify (inputString) { + if (typeof inputString !== 'string') { + return ''; + } + + inputString = inputString.toLowerCase(); + + return inputString.charAt(0).toUpperCase() + inputString.slice(1); +} + +/** + * sanitizes input options + * + * @param {Object} options + * @param {Array} optionsArray + * + * @returns {Object} - Sanitized options object + */ +function sanitizeOptions (options, optionsArray) { + var result = {}, + defaultOptions = {}, + id; + optionsArray.forEach((option) => { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + for (id in options) { + if (options.hasOwnProperty(id)) { + if (defaultOptions[id] === undefined) { + continue; + } + switch (defaultOptions[id].type) { + case 'boolean': + if (typeof options[id] !== 'boolean') { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'positiveInteger': + if (typeof options[id] !== 'number' || options[id] < 0) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'enum': + if (!defaultOptions[id].availableOptions.includes(options[id])) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + default: + result[id] = options[id]; + } + } + } + + for (id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + if (result[id] === undefined) { + result[id] = defaultOptions[id].default; + } + } + } + return result; +} + +module.exports = { + sanitize: sanitize, + csharpify: csharpify, + sanitizeOptions: sanitizeOptions +}; diff --git a/codegens/csharp-httpclient/npm-shrinkwrap.json b/codegens/csharp-httpclient/npm-shrinkwrap.json new file mode 100644 index 000000000..e5ddf84d1 --- /dev/null +++ b/codegens/csharp-httpclient/npm-shrinkwrap.json @@ -0,0 +1,5 @@ +{ + "name": "@postman/codegen-csharp-httpclient", + "version": "0.0.1", + "lockfileVersion": 1 +} diff --git a/codegens/csharp-httpclient/npm/test-lint.js b/codegens/csharp-httpclient/npm/test-lint.js new file mode 100644 index 000000000..2f4db0cb8 --- /dev/null +++ b/codegens/csharp-httpclient/npm/test-lint.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +var shell = require('shelljs'), + chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/csharp-httpclient/npm/test-newman.js b/codegens/csharp-httpclient/npm/test-newman.js new file mode 100644 index 000000000..0c8559a8e --- /dev/null +++ b/codegens/csharp-httpclient/npm/test-newman.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'newman'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running newman tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/csharp-httpclient/npm/test-unit.js b/codegens/csharp-httpclient/npm/test-unit.js new file mode 100644 index 000000000..e27d1a223 --- /dev/null +++ b/codegens/csharp-httpclient/npm/test-unit.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// -------------------------------------------------------------- +// This script is intended to execute all unit tests +// -------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit'); + +module.exports = function (exit) { + // banner line + console.log(chalk.yellow.bold('Running unit tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spe files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/csharp-httpclient/npm/test.js b/codegens/csharp-httpclient/npm/test.js new file mode 100644 index 000000000..b08dad176 --- /dev/null +++ b/codegens/csharp-httpclient/npm/test.js @@ -0,0 +1,18 @@ +#!/usr/bin/env node +var chalk = require('chalk'), + exit = require('shelljs').exit, + prettyms = require('pretty-ms'), + startedAt = Date.now(), + name = require('../package.json').name; + +require('async').series([ + require('./test-lint'), + require('./test-unit'), + require('./test-newman') + // require('./test-browser') + // require('./test-integration') +], function (code) { + // eslint-disable-next-line max-len + console.info(chalk[code ? 'red' : 'green'](`\n${name}: duration ${prettyms(Date.now() - startedAt)}\n${name}: ${code ? 'not ok' : 'ok'}!`)); + exit(code && (typeof code === 'number' ? code : 1) || 0); +}); diff --git a/codegens/csharp-httpclient/package.json b/codegens/csharp-httpclient/package.json new file mode 100644 index 000000000..1eaf39709 --- /dev/null +++ b/codegens/csharp-httpclient/package.json @@ -0,0 +1,34 @@ +{ + "name": "@postman/codegen-csharp-httpclient", + "version": "0.0.1", + "description": "Converts Postman SDK Requests to csharp-httpclient code snippet", + "main": "index.js", + "com_postman_plugin": { + "type": "code_generator", + "lang": "csharp", + "variant": "HttpClient", + "syntax_mode": "csharp" + }, + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "node npm/test.js", + "test-lint": "node npm/test-lint.js", + "test-unit": "node npm/test-unit.js", + "test-newman": "node npm/test-newman.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "Postman Labs ", + "license": "Apache-2.0", + "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/csharp-httpclient", + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=8" + } +} diff --git a/codegens/csharp-httpclient/test/.eslintrc b/codegens/csharp-httpclient/test/.eslintrc new file mode 100644 index 000000000..9d477e31e --- /dev/null +++ b/codegens/csharp-httpclient/test/.eslintrc @@ -0,0 +1,30 @@ +{ + "plugins": [ + "mocha" + ], + "env": { + "mocha": true, + "node": true, + "es6": true + }, + "rules": { + // Mocha + "mocha/handle-done-callback": "error", + "mocha/max-top-level-suites": "error", + "mocha/no-exclusive-tests": "error", + "mocha/no-global-tests": "error", + "mocha/no-hooks-for-single-case": "off", + "mocha/no-hooks": "off", + "mocha/no-identical-title": "error", + "mocha/no-mocha-arrows": "error", + "mocha/no-nested-tests": "error", + "mocha/no-pending-tests": "error", + "mocha/no-return-and-callback": "error", + "mocha/no-sibling-hooks": "error", + "mocha/no-skipped-tests": "warn", + "mocha/no-synchronous-tests": "off", + "mocha/no-top-level-hooks": "warn", + "mocha/valid-test-description": "off", + "mocha/valid-suite-description": "off" + } +} diff --git a/codegens/csharp-httpclient/test/ci-install.sh b/codegens/csharp-httpclient/test/ci-install.sh new file mode 100755 index 000000000..0dac46ef4 --- /dev/null +++ b/codegens/csharp-httpclient/test/ci-install.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -ev; # stop on error + +sudo apt-get update +echo "Installing dependencies required for tests in codegens/csharp-httpclient" +# Install latest .net8.0 sdk +pushd ./codegens/csharp-httpclient &>/dev/null; + wget -q https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + sudo apt-get install apt-transport-https + sudo apt-get update + sudo apt-get install dotnet-sdk-8.0 + dotnet new console -o testProject -f net8.0 + # no extra packages needed +popd &>/dev/null; diff --git a/codegens/csharp-httpclient/test/newman/newman.test.js b/codegens/csharp-httpclient/test/newman/newman.test.js new file mode 100644 index 000000000..30f2e9f18 --- /dev/null +++ b/codegens/csharp-httpclient/test/newman/newman.test.js @@ -0,0 +1,21 @@ +var path = require('path'), + runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, + convert = require('../../index').convert; + + +describe('convert for different request types', function () { + var projectPath = path.resolve(__dirname, '../../testProject'), + testConfig = { + // filename along with the appropriate version of the file. This file will be used to run the snippet. + fileName: projectPath + '/Program.cs', + // Run script required to run the generated code snippet + runScript: 'dotnet run --project ' + projectPath, + // Compile script required to compile the code snippet + compileScript: 'dotnet build ' + projectPath, + skipCollections: ['unsupportedMethods'] + }, + options = { + includeBoilerplate: true + }; + runNewmanTest(convert, options, testConfig); +}); diff --git a/codegens/csharp-httpclient/test/unit/convert.test.js b/codegens/csharp-httpclient/test/unit/convert.test.js new file mode 100644 index 000000000..1a8721b2e --- /dev/null +++ b/codegens/csharp-httpclient/test/unit/convert.test.js @@ -0,0 +1,534 @@ +var expect = require('chai').expect, + { Request } = require('postman-collection/lib/collection/request'), + convert = require('../../lib/index').convert, + mainCollection = require('./fixtures/testcollection/collection.json'), + testCollection = require('./fixtures/testcollection/collectionForEdge.json'), + getOptions = require('../../lib/index').getOptions, + testResponse = require('./fixtures/testresponse.json'), + sanitize = require('../../lib/util').sanitize, + sanitizeOptions = require('../../lib/util').sanitizeOptions; +// csharpify = require('../../lib/util').csharpify; + +describe('csharp httpclient function', function () { + + describe('csharp-httpclient convert function', function () { + it('should return expected snippet', function () { + var request = new Request(mainCollection.item[10].request), + options = { + indentCount: 1, + indentType: 'Tab' + }; + + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + return; + } + expect(snippet).deep.equal(testResponse.result); + }); + }); + }); + + describe('convert function', function () { + var request = new Request(testCollection.item[0].request), + snippetArray, + options = { + includeBoilerplate: true, + indentType: 'Space', + indentCount: 2 + }; + + // it('should return snippet with boilerplate code given option', function () { + // convert(request, { includeBoilerplate: true }, function (error, snippet) { + // if (error) { + // expect.fail(null, null, error); + // return; + // } + // expect(snippet).to.include('using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n' + + // 'namespace HelloWorldApplication\n{\n public class Program\n {\n ' + + // 'static async Task Main(string[] args)\n {'); + // }); + // }); + + it('should generate snippet with Space as indent type with exact indent count', function () { + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + return; + } + snippetArray = snippet.split('\n'); + for (var i = 0; i < snippetArray.length; i++) { + if (snippetArray[i].startsWith('namespace HelloWorldApplication')) { + expect(snippetArray[i + 1].charAt(0)).to.equal('{'); + // TODO: Do more expects + expect(snippetArray[i + 2].charAt(0)).to.equal(' '); + } + } + }); + }); + + it('should add client timeout configurations when requestTimeout is set to non zero value', function () { + convert(request, { requestTimeout: 5 }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('client.Timeout = TimeSpan.FromSeconds(5);'); + }); + }); + + it('should add client FollowRedirects configurations when followRedirects is set to false', function () { + convert(request, { followRedirect: false }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).to.be.a('string'); + expect(snippet).to.include('AllowAutoRedirect = false'); + }); + }); + + it('should create custom HttpMethod when method is non-standard', function () { + var request = new Request({ + 'method': 'NOTNORMAL', + 'header': [], + 'url': { + 'raw': 'https://google.com', + 'protocol': 'https', + 'host': [ + 'google', + 'com' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('new HttpMethod("NOTNORMAL")'); + }); + }); + + it('should throw when callback is not a function', function () { + expect(function () { convert(request, {}, 'not a function'); }) + .to.throw('C#-HttpClient-Converter: Callback is not valid function'); + }); + + it('should add fake body when content type header added to empty body', function () { + var request = new Request({ + 'method': 'DELETE', + 'body': {}, + 'header': [ + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ] + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.include('var content = new StringContent(string.Empty);'); + expect(snippet).to.include('content.Headers.ContentType = new MediaTypeHeaderValue(' + + '"application/json");'); + }); + }); + + // it('should only include one System.IO using with multiple files', function () { + // var request = new Request({ + // 'method': 'POST', + // 'header': [], + // 'body': { + // 'mode': 'formdata', + // 'formdata': [ + // { + // 'key': 'no file', + // 'value': '', + // 'type': 'file', + // 'src': '/test1.txt' + // }, + // { + // 'key': 'no file', + // 'value': '', + // 'type': 'file', + // 'src': '/test2.txt' + // } + // ] + // } + // }); + // convert(request, { includeBoilerplate: true, indentCount: 2, indentType: 'Space' }, function (error, snippet) { + // if (error) { + // expect.fail(null, null, error); + // } + // expect(snippet).to.include('using System;\nusing System.IO;\nusing System.Net.Http;\n'); + // expect(snippet).to + // .include('content.Add(new StreamContent(File.OpenRead("/test1.txt")), "no file", "/test1.txt");'); + // expect(snippet).to + // .include('content.Add(new StreamContent(File.OpenRead("/test2.txt")), "no file", "/test2.txt");'); + // }); + // }); + + it('should include multiple form content when file has multiple sources', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'no file', + 'value': '', + 'type': 'file', + 'src': [ + '/test1.txt', + '/test2.txt' + ] + } + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to + .include('content.Add(new StreamContent(File.OpenRead("/test1.txt")), "no file", "/test1.txt");'); + expect(snippet).to + .include('content.Add(new StreamContent(File.OpenRead("/test2.txt")), "no file", "/test2.txt");'); + }); + }); + + it('should include graphql body in the snippet', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{ body { graphql } }', + 'variables': '{"variable_key": "variable_value"}' + } + }, + 'url': { + 'raw': 'http://postman-echo.com/post', + 'protocol': 'http', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to + .include('var content = new StringContent("{\\"query\\":\\"{ body { graphql } }\\",' + + '\\"variables\\":{\\"variable_key\\":\\"variable_value\\"}}", null, "application/json");'); + + }); + }); + + it('should add blank graphql variables when invalid', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{ body { graphql } }', + 'variables': 'some xml' + } + }, + 'url': { + 'raw': 'http://postman-echo.com/post', + 'protocol': 'http', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to + .include('var content = new StringContent("{\\"query\\":\\"{ body { graphql } }\\",' + + '\\"variables\\":{}}", null, "application/json");'); + + }); + }); + + it('should not add multiport form content when disabled', function () { + var request = new Request(mainCollection.item[15].request); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('content.Add(new StringContent("\'a\'"), "pl");'); + expect(snippet).to.include('content.Add(new StringContent("\\"b\\""), "qu");'); + expect(snippet).to.include('content.Add(new StringContent("d"), "sa");'); + expect(snippet).to.include('content.Add(new StringContent("!@#$%&*()^_+=`~"), "Special");'); + expect(snippet).to.include('content.Add(new StringContent(",./\';[]}{\\":?><|\\\\\\\\"), "more");'); + expect(snippet).not.to.include('Not Select'); + expect(snippet).not.to.include('Disabled'); + }); + }); + + it('should run add content as string on raw request', function () { + var request = new Request(mainCollection.item[12].request); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to + .include('var content = new StringContent("Curabitur auctor, elit nec pulvinar porttitor, ' + + 'ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet' + + ' luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. ' + + 'Nam quis congue mi. Etiam volutpat.", null, "text/plain");'); + }); + }); + + it('should add a file on file request', function () { + var request = new Request({ + 'method': 'POST', + 'url': 'https://google.com', + 'header': [], + 'body': { + 'mode': 'file', + 'file': { + 'src': './test.txt' + } + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('request.Content = new StreamContent(File.OpenRead("./test.txt"));'); + }); + }); + + it('should add all enabled headers to request', function () { + var request = new Request({ + 'method': 'POST', + 'url': 'https://postman-echo.com/post', + 'header': [ + { + 'key': 'my-header', + 'value': 'my-header-value' + }, + { + 'key': 'Content-Type', + 'value': 'application/json' + }, + { + 'key': 'disabled-header', + 'value': 'diabled-header-value', + 'disabled': true + } + ], + 'body': { + 'mode': 'raw', + 'raw': '{\n "json": "Test-Test"\n}' + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('request.Headers.Add("my-header", "my-header-value");'); + expect(snippet).not.to.include('Content-Type'); + expect(snippet).not.to.include('disabled-header'); + expect(snippet).not.to.include('disabled-header-value'); + }); + }); + + it('should skip disabled form url encoded values', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'url': 'https://postman-echo.com/post', + 'body': { + 'mode': 'urlencoded', + 'urlencoded': [ + { + 'key': 'enabled', + 'value': 'enabled-value' + }, + { + 'key': 'disabled', + 'value': 'disabled-value', + 'disabled': true + } + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('collection.Add(new("enabled", "enabled-value"));'); + expect(snippet).not.to.include('disabled'); + }); + }); + + it('should skip collection initialization when no urlencoded values are enabled', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'url': 'https://postman-echo.com/post', + 'body': { + 'mode': 'urlencoded', + 'urlencoded': [ + { + 'key': 'disabled', + 'value': 'disabled-value', + 'disabled': true + } + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).not.to.include('var collection = new List>();'); + }); + }); + + it('should skip creating multipart form data content when all values are disabled', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'url': 'https://postman-echo.com/post', + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'disabled', + 'value': 'disabled-value', + 'disabled': true + } + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).not.to.include('var content = new MultipartFormDataContent();'); + }); + }); + }); + + describe('getOptions function', function () { + it('should return array of options for csharp-httpclient converter', function () { + expect(getOptions()).to.be.an('array'); + }); + + it('should return all the valid options', function () { + expect(getOptions()[0]).to.have.property('id', 'includeBoilerplate'); + expect(getOptions()[1]).to.have.property('id', 'indentCount'); + expect(getOptions()[2]).to.have.property('id', 'indentType'); + expect(getOptions()[3]).to.have.property('id', 'requestTimeout'); + expect(getOptions()[4]).to.have.property('id', 'followRedirect'); + }); + }); + + describe('Sanitize function', function () { + it('should return empty string when input is not a string type', function () { + expect(sanitize(123, false)).to.equal(''); + expect(sanitize(null, false)).to.equal(''); + expect(sanitize({}, false)).to.equal(''); + expect(sanitize([], false)).to.equal(''); + }); + + it('should trim input string when needed', function () { + expect(sanitize('inputString ', true)).to.equal('inputString'); + }); + }); + + describe('sanitizeOptions function', function () { + var defaultOptions = {}, + testOptions = {}, + sanitizedOptions; + + getOptions().forEach((option) => { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + + it('should remove option not supported by module', function () { + testOptions.randomName = 'random value'; + sanitizedOptions = sanitizeOptions(testOptions, getOptions()); + expect(sanitizedOptions).to.not.have.property('randomName'); + }); + + it('should use defaults when option value type does not match with expected type', function () { + testOptions = {}; + testOptions.indentCount = '5'; + testOptions.includeBoilerplate = 'true'; + testOptions.indentType = 'tabSpace'; + sanitizedOptions = sanitizeOptions(testOptions, getOptions()); + expect(sanitizedOptions.indentCount).to.equal(defaultOptions.indentCount.default); + expect(sanitizedOptions.includeBoilerplate).to.equal(defaultOptions.includeBoilerplate.default); + expect(sanitizedOptions.indentType).to.equal(defaultOptions.indentType.default); + }); + + it('should use defaults when option type is valid but value is invalid', function () { + testOptions = {}; + testOptions.indentCount = -1; + testOptions.indentType = 'spaceTab'; + testOptions.requestTimeout = -3000; + sanitizedOptions = sanitizeOptions(testOptions, getOptions()); + expect(sanitizedOptions.indentCount).to.equal(defaultOptions.indentCount.default); + expect(sanitizedOptions.indentType).to.equal(defaultOptions.indentType.default); + expect(sanitizedOptions.requestTimeout).to.equal(defaultOptions.requestTimeout.default); + }); + + it('should return the same object when default options are provided', function () { + for (var id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + testOptions[id] = defaultOptions[id].default; + } + } + sanitizedOptions = sanitizeOptions(testOptions, getOptions()); + expect(sanitizedOptions).to.deep.equal(testOptions); + }); + + it('should return the same object when valid (but not necessarily defaults) options are provided', function () { + testOptions = {}; + testOptions.indentType = 'Tab'; + testOptions.indentCount = 3; + testOptions.requestTimeout = 3000; + testOptions.followRedirect = false; + testOptions.includeBoilerplate = true; + sanitizedOptions = sanitizeOptions(testOptions, getOptions()); + expect(sanitizedOptions).to.deep.equal(testOptions); + }); + }); +}); diff --git a/codegens/csharp-httpclient/test/unit/csharpify.test.js b/codegens/csharp-httpclient/test/unit/csharpify.test.js new file mode 100644 index 000000000..ca9f807d3 --- /dev/null +++ b/codegens/csharp-httpclient/test/unit/csharpify.test.js @@ -0,0 +1,21 @@ +var expect = require('chai').expect, + csharpify = require('../../lib/util').csharpify; + +describe('csharpify function', function () { + const THEORIES = [ + ['test', 'Test'], + ['TEST', 'Test'], + ['TeSt', 'Test'], + ['', ''], + [123, ''], + [{}, ''], + [[], ''], + [null, ''] + ]; + + THEORIES.forEach(function ([input, expected]) { + it(`Should transform ${input} into ${expected}`, function () { + expect(csharpify(input)).equal(expected); + }); + }); +}); diff --git a/codegens/csharp-httpclient/test/unit/fixtures/testcollection/collection.json b/codegens/csharp-httpclient/test/unit/fixtures/testcollection/collection.json new file mode 100644 index 000000000..89c069bd4 --- /dev/null +++ b/codegens/csharp-httpclient/test/unit/fixtures/testcollection/collection.json @@ -0,0 +1,781 @@ +{ + "info": { + "_postman_id": "f52ee07d-6345-4220-89af-e6696b3c0122", + "name": "Basic Collection", + "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Request Headers (With special Characters)", + "event": [ + { + "listen": "test", + "script": { + "id": "34edbfa7-7d32-42d6-8397-af2378c3aaa4", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "TEST", + "value": "@#$%^&*()" + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\" + } + ], + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "Request Headers", + "event": [], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "testing", + "value": "'singlequotes'" + }, + { + "key": "TEST", + "value": "\"doublequotes\"" + }, + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "Request Headers with disabled headers", + "event": [ + { + "listen": "test", + "script": { + "id": "e150d55b-0273-430a-9e1d-11969b433734", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "not-disabled-header", + "value": "ENABLED" + }, + { + "key": "disabled header", + "value": "DISABLED", + "disabled": true + } + ], + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "GET Request with disabled query", + "event": [ + { + "listen": "test", + "script": { + "id": "1bfe1fc3-c244-4a42-83c5-1a0d94d56ffd", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/get?test=123&anotherone=232", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "test", + "value": "123" + }, + { + "key": "anotherone", + "value": "232" + }, + { + "key": "anotheroneone", + "value": "sdfsdf", + "disabled": true + } + ] + }, + "description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested." + }, + "response": [] + }, + { + "name": "POST Raw Text", + "event": [ + { + "listen": "test", + "script": { + "id": "a3ddecd1-e89d-426d-995c-0d6a678caa91", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 281);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/plain", + "disabled": false + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat. !@#$%^&*()_+-=,.<>/?" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST json with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "e926912d-1c99-4c54-9b53-91c8f63acef0", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST graphql body(json) with raw", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "graphql", + "graphql": { + "query": "{\n body{\n graphql\n }\n}", + "variables": "{\n\t\"variable_key\": \"variable_value\"\n}" + } + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "POST javascript with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "d211bdad-60b3-4cd6-869f-853377bf03ef", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/javascript" + } + ], + "body": { + "mode": "raw", + "raw": "var val = 6;\nconsole.log(val);console.log('$text\r\n');\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/xml with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "532fef57-48fd-4ffe-ac7e-f5a7e32facc2", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/xml" + } + ], + "body": { + "mode": "raw", + "raw": "\n\tTest Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/html with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "8bbbbc5b-2983-4979-8347-3ced95a69f7e", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/html" + } + ], + "body": { + "mode": "raw", + "raw": "\n Test Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST urlencoded data", + "event": [ + { + "listen": "test", + "script": { + "id": "48da0505-470f-4cf3-bb77-30665415af60", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "1", + "value": "'a'", + "type": "text" + }, + { + "key": "2", + "value": "\"b\"", + "type": "text" + }, + { + "key": "'3'", + "value": "c", + "type": "text" + }, + { + "key": "\"4\"", + "value": "d", + "type": "text" + }, + { + "key": "Special", + "value": "!@#$%*()^_=`&~ ", + "type": "text" + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\ ", + "type": "text" + }, + { + "key": "non-ascii", + "value": "테스트", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "PUT Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + }, + "description": "The HTTP `PUT` request method is similar to HTTP `POST`. It too is meant to \ntransfer data to a server (and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `PUT` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following \nraw HTTP request,\n\n> PUT /hi/there?hand=wave\n>\n> \n\n\n" + }, + "response": [] + }, + { + "name": "PATCH Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." + }, + "url": { + "raw": "https://postman-echo.com/patch", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "patch" + ] + }, + "description": "The HTTP `PATCH` method is used to update resources on a server. The exact\nuse of `PATCH` requests depends on the server in question. There are a number\nof server implementations which handle `PATCH` differently. Technically, \n`PATCH` supports both Query String parameters and a Request Body.\n\nThis endpoint accepts an HTTP `PATCH` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "DELETE Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": "The HTTP `DELETE` method is used to delete resources on a server. The exact\nuse of `DELETE` requests depends on the server implementation. In general, \n`DELETE` requests support both, Query String parameters as well as a Request \nBody.\n\nThis endpoint accepts an HTTP `DELETE` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "Single/multiple file upload via form-data", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "single file", + "value": "", + "type": "file", + "src": "/Users/justinbaur/Developer/postman-code-generators/dummyFile1.txt" + }, + { + "key": "multiple files", + "value": "", + "type": "file", + "src": [ + "/Users/justinbaur/Developer/postman-code-generators/dummyFile2.txt", + "/Users/justinbaur/Developer/postman-code-generators/dummyFile3.txt" + ] + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + } + }, + { + "name": "POST multipart/form-data with text parameters", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "pl", + "value": "'a'", + "type": "text" + }, + { + "key": "qu", + "value": "\"b\"", + "type": "text" + }, + { + "key": "sa", + "value": "d", + "type": "text" + }, + { + "key": "Special", + "value": "!@#$%&*()^_+=`~", + "type": "text" + }, + { + "key": "Not Select", + "value": "Disabled", + "type": "text", + "disabled": true + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "e80b6162-6c90-4150-bfa1-7f42f11c8f64", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "538efa04-97ce-456c-a5a1-772c466591d5", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] +} diff --git a/codegens/csharp-httpclient/test/unit/fixtures/testcollection/collectionForEdge.json b/codegens/csharp-httpclient/test/unit/fixtures/testcollection/collectionForEdge.json new file mode 100644 index 000000000..1087bf5ba --- /dev/null +++ b/codegens/csharp-httpclient/test/unit/fixtures/testcollection/collectionForEdge.json @@ -0,0 +1,38 @@ +{ + "info": { + "_postman_id": "aeb25545-7a14-44fc-b7e9-d818a3f9a4cf", + "name": "collectionForEdge", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Sample test case", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "fdjks", + "value": "dsf", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + } + ] +} diff --git a/codegens/csharp-httpclient/test/unit/fixtures/testresponse.json b/codegens/csharp-httpclient/test/unit/fixtures/testresponse.json new file mode 100644 index 000000000..60a187b30 --- /dev/null +++ b/codegens/csharp-httpclient/test/unit/fixtures/testresponse.json @@ -0,0 +1,3 @@ +{ + "result": "var client = new HttpClient();\nvar request = new HttpRequestMessage(HttpMethod.Post, \"https://postman-echo.com/post\");\nvar collection = new List>();\ncollection.Add(new(\"1\", \"'a'\"));\ncollection.Add(new(\"2\", \"\\\"b\\\"\"));\ncollection.Add(new(\"'3'\", \"c\"));\ncollection.Add(new(\"\\\"4\\\"\", \"d\"));\ncollection.Add(new(\"Special\", \"!@#$%*()^_=`&~ \"));\ncollection.Add(new(\"more\", \",./';[]}{\\\":?><|\\\\\\\\ \"));\ncollection.Add(new(\"non-ascii\", \"테스트\"));\nvar content = new FormUrlEncodedContent(collection);\nrequest.Content = content;\nvar response = await client.SendAsync(request);\nresponse.EnsureSuccessStatusCode();\nConsole.WriteLine(await response.Content.ReadAsStringAsync());\n" +} diff --git a/codegens/csharp-restsharp/.gitignore b/codegens/csharp-restsharp/.gitignore index 9d9b068fb..061d1c7b2 100644 --- a/codegens/csharp-restsharp/.gitignore +++ b/codegens/csharp-restsharp/.gitignore @@ -8,6 +8,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/csharp-restsharp/lib/parseRequest.js b/codegens/csharp-restsharp/lib/parseRequest.js index c1d771c6b..c79d2e3c4 100644 --- a/codegens/csharp-restsharp/lib/parseRequest.js +++ b/codegens/csharp-restsharp/lib/parseRequest.js @@ -41,6 +41,49 @@ function parseContentType (request) { return request.getHeaders({enabled: true, ignoreCase: true})['content-type'] || 'text/plain'; } +/** + * Generates a parameter using AddStringBody method + * + * @param {Object} requestBody - JSON object representing body of request + * @param {string} dataFormat - the data format to use "DataFormat.Json" or "DataFormat.Xml" + * @returns {String} snippet of the parameter generation + */ +function getAddStringBodyParam (requestBody, dataFormat) { + return `var body = ${requestBody[requestBody.mode] + .split('\n') + .map((line) => { return '@"' + line.replace(/"/g, '""') + '"'; }) + .join(' + "\\n" +\n')};\n` + + `request.AddStringBody(body, ${dataFormat});\n`; +} + +/** + * Parses Raw data + * + * @param {Object} request - JSON object representing body of request + * @param {Object} requestBody - JSON object representing body of request + * @returns {String} snippet of the body generation + */ +function parseRawBody (request, requestBody) { + let bodySnippet = '', + contentType = parseContentType(request); + if (contentType && (contentType === 'application/json' || contentType.match(/\+json$/))) { + bodySnippet = getAddStringBodyParam(requestBody, 'DataFormat.Json'); + } + else if (contentType && (contentType === 'text/xml' || contentType.match(/\+xml$/))) { + bodySnippet = getAddStringBodyParam(requestBody, 'DataFormat.Xml'); + } + else { + bodySnippet = `var body = ${requestBody[requestBody.mode] + .split('\n') + .map((line) => { return '@"' + line.replace(/"/g, '""') + '"'; }) + .join(' + "\\n" +\n')};\n` + + `request.AddParameter("${contentType}", ` + + 'body, ParameterType.RequestBody);\n'; + } + + return bodySnippet; +} + /** * * @param {Object} requestBody - JSON object representing body of request @@ -78,12 +121,7 @@ function parseBody (request, trimFields) { case 'formdata': return parseFormData(requestBody, trimFields); case 'raw': - return `var body = ${requestBody[requestBody.mode] - .split('\n') - .map((line) => { return '@"' + line.replace(/"/g, '""') + '"'; }) - .join(' + "\\n" +\n')};\n` + - `request.AddParameter("${parseContentType(request)}", ` + - 'body, ParameterType.RequestBody);\n'; + return parseRawBody(request, requestBody); case 'graphql': return parseGraphQL(requestBody, trimFields); /* istanbul ignore next */ @@ -110,10 +148,7 @@ function parseHeader (requestJson) { return requestJson.header.reduce((headerSnippet, header) => { if (!header.disabled) { - if (sanitize(header.key, true).toLowerCase() === 'user-agent') { - headerSnippet += `client.UserAgent = "${sanitize(header.value)}";\n`; - } - else { + if (sanitize(header.key, true).toLowerCase() !== 'user-agent') { headerSnippet += `request.AddHeader("${sanitize(header.key, true)}", "${sanitize(header.value)}");\n`; } } diff --git a/codegens/csharp-restsharp/lib/restsharp.js b/codegens/csharp-restsharp/lib/restsharp.js index ec0b86bc8..46c7b2a7f 100644 --- a/codegens/csharp-restsharp/lib/restsharp.js +++ b/codegens/csharp-restsharp/lib/restsharp.js @@ -4,32 +4,100 @@ var _ = require('./lodash'), sanitize = require('./util').sanitize, sanitizeOptions = require('./util').sanitizeOptions, addFormParam = require('./util').addFormParam, + { URL } = require('url'), self; /** - * Generates snippet in csharp-restsharp by parsing data from Postman-SDK request object +* Takes in a string and returns a new string with only the first character capitalized +* +* @param {string} string - string to change +* @returns {String} - the same string with the first litter as capital letter +*/ +function capitalizeFirstLetter (string) { + return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); +} + +/** + * Generates snippet for the RestClientOptions object * - * @param {Object} request - Postman SDK request object + * @param {string} urlOrigin - String representing the origin of the url * @param {Object} options - Options to tweak code snippet - * @returns {String} csharp-restsharp code snippet for given request object + * @param {string} indentString - String representing value of indentation required + * @param {Array} headers - request headers + * @returns {String} csharp-restsharp RestClientOptions object snippet */ -function makeSnippet (request, options) { - const UNSUPPORTED_METHODS_LIKE_POST = ['LINK', 'UNLINK', 'LOCK', 'PROPFIND'], - UNSUPPORTED_METHODS_LIKE_GET = ['PURGE', 'UNLOCK', 'VIEW', 'COPY']; +function makeOptionsSnippet (urlOrigin, options, indentString, headers) { + let userAgentHeader, + snippet = `var options = new RestClientOptions("${sanitize(urlOrigin)}")\n{\n`; + if (Array.isArray(headers)) { + userAgentHeader = headers.find((header) => { + return (!header.disabled && sanitize(header.key, true).toLowerCase() === 'user-agent'); - var snippet = `var client = new RestClient("${sanitize(request.url.toString())}");\n`, - isUnSupportedMethod = UNSUPPORTED_METHODS_LIKE_GET.includes(request.method) || - UNSUPPORTED_METHODS_LIKE_POST.includes(request.method); + }); + } if (options.requestTimeout) { - snippet += `client.Timeout = ${options.requestTimeout};\n`; + snippet += `${indentString}MaxTimeout = ${options.requestTimeout},\n`; } else { - snippet += 'client.Timeout = -1;\n'; + snippet += `${indentString}MaxTimeout = -1,\n`; } if (!options.followRedirect) { - snippet += 'client.FollowRedirects = false;\n'; + snippet += `${indentString}FollowRedirects = false,\n`; + } + if (userAgentHeader) { + snippet += `${indentString}UserAgent = "${userAgentHeader.value}",\n`; + } + snippet += '};\n'; + return snippet; +} + +/** + * Generates an URL object from the string + * + * @param {string} stringToParse - url in string representation + * @returns {object} the URL object + */ +function parseURL (stringToParse) { + try { + let objectURL = new URL(stringToParse); + return objectURL; + } + catch (err) { + try { + var url = require('url'); + let urlObj = url.parse(stringToParse); + if (urlObj.hostname === null) { + return false; + } + return urlObj; + } + catch (parseErr) { + return false; + } } - snippet += `var request = new RestRequest(${isUnSupportedMethod ? '' : ('Method.' + request.method)});\n`; +} + +/** + * Generates snippet in csharp-restsharp by parsing data from Postman-SDK request object + * + * @param {Object} request - Postman SDK request object + * @param {Object} options - Options to tweak code snippet + * @param {string} indentString - String representing value of indentation required + * @returns {string} csharp-restsharp code snippet for given request object + */ +function makeSnippet (request, options, indentString) { + const UNSUPPORTED_METHODS_LIKE_POST = ['LINK', 'UNLINK', 'LOCK', 'PROPFIND'], + UNSUPPORTED_METHODS_LIKE_GET = ['PURGE', 'UNLOCK', 'VIEW'], + isUnSupportedMethod = UNSUPPORTED_METHODS_LIKE_GET.includes(request.method) || + UNSUPPORTED_METHODS_LIKE_POST.includes(request.method), + url = parseURL(request.url.toString()), + urlOrigin = url ? parseURL(request.url.toString()).origin : request.url.toString(), + urlPathAndHash = url ? request.url.toString().replace(urlOrigin, '') : ''; + + let snippet = makeOptionsSnippet(urlOrigin, options, indentString, request.toJSON().header); + snippet += 'var client = new RestClient(options);\n'; + snippet += `var request = new RestRequest("${sanitize(urlPathAndHash)}", ` + + `${isUnSupportedMethod ? 'Method.Get' : ('Method.' + capitalizeFirstLetter(request.method))});\n`; if (request.body && request.body.mode === 'graphql' && !request.headers.has('Content-Type')) { request.addHeader({ key: 'Content-Type', @@ -38,17 +106,11 @@ function makeSnippet (request, options) { } snippet += parseRequest.parseHeader(request.toJSON(), options.trimRequestBody); if (request.body && request.body.mode === 'formdata') { - let isFile = false, - formdata = request.body.formdata, + let formdata = request.body.formdata, formdataArray = []; - request.body.toJSON().formdata.forEach((data) => { - if (!data.disabled && data.type === 'file') { - isFile = true; - } - }); // The following statement needs to be added else the multipart/form-data request where there is no file // is being sent as x-www-form-urlencoded by default - if (!isFile) { + if (formdata.members.length > 0) { snippet += 'request.AlwaysMultipartFormData = true;\n'; } @@ -92,14 +154,14 @@ function makeSnippet (request, options) { } snippet += parseRequest.parseBody(request, options.trimRequestBody); if (isUnSupportedMethod) { - (UNSUPPORTED_METHODS_LIKE_GET.includes(request.method)) && - (snippet += `IRestResponse response = client.ExecuteAsGet(request, "${request.method}");\n`); - (UNSUPPORTED_METHODS_LIKE_POST.includes(request.method)) && - (snippet += `IRestResponse response = client.ExecuteAsPost(request, "${request.method}");\n`); - } - else { - snippet += 'IRestResponse response = client.Execute(request);\n'; + snippet += 'request.OnBeforeRequest = (request) =>\n'; + snippet += '{\n'; + snippet += `${indentString}request.Method = new HttpMethod("${request.method}");\n`; + snippet += `${indentString}return default;\n`; + snippet += '};\n'; } + + snippet += 'RestResponse response = await client.ExecuteAsync(request);\n'; snippet += 'Console.WriteLine(response.Content);'; return snippet; @@ -192,7 +254,8 @@ self = module.exports = { // snippets to include C# class definition according to options headerSnippet = '', footerSnippet = '', - + mainMethodSnippet = '', + importTask = '', // snippet to create request in csharp-restsharp snippet = ''; @@ -202,15 +265,20 @@ self = module.exports = { indentString = indentString.repeat(options.indentCount); if (options.includeBoilerplate) { + + mainMethodSnippet = indentString.repeat(2) + 'static async Task Main(string[] args) {\n'; + importTask = 'using System.Threading;\nusing System.Threading.Tasks;\n'; + headerSnippet = 'using System;\n' + 'using RestSharp;\n' + + importTask + 'namespace HelloWorldApplication {\n' + indentString + 'class HelloWorld {\n' + - indentString.repeat(2) + 'static void Main(string[] args) {\n'; + mainMethodSnippet; footerSnippet = indentString.repeat(2) + '}\n' + indentString + '}\n}\n'; } - snippet = makeSnippet(request, options); + snippet = makeSnippet(request, options, indentString); // if boilerplate is included then two more indentString needs to be added in snippet (options.includeBoilerplate) && diff --git a/codegens/csharp-restsharp/test/ci-install.sh b/codegens/csharp-restsharp/test/ci-install.sh new file mode 100755 index 000000000..7ceff201c --- /dev/null +++ b/codegens/csharp-restsharp/test/ci-install.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -ev; # stop on error + +sudo apt-get update +echo "Installing dependencies required for tests in codegens/csharp-restsharp" +pushd ./codegens/csharp-restsharp &>/dev/null; + wget -q https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb + sudo dpkg -i packages-microsoft-prod.deb + sudo apt-get install apt-transport-https + sudo apt-get update + sudo apt-get install dotnet-sdk-8.0 + dotnet new console -o testProject -f net8.0 + pushd ./testProject &>/dev/null; + dotnet add package RestSharp --version 112.0.0 + popd &>/dev/null; +popd &>/dev/null; + +sudo apt-get install -y mono-complete diff --git a/codegens/csharp-restsharp/test/unit/convert.test.js b/codegens/csharp-restsharp/test/unit/convert.test.js index dafd37385..636da51d7 100644 --- a/codegens/csharp-restsharp/test/unit/convert.test.js +++ b/codegens/csharp-restsharp/test/unit/convert.test.js @@ -1,18 +1,19 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), convert = require('../../lib/index').convert, mainCollection = require('./fixtures/testcollection/collection.json'), testCollection = require('./fixtures/testcollection/collectionForEdge.json'), getOptions = require('../../lib/index').getOptions, - testResponse = require('./fixtures/testresponse.json'), + testResponseAsync = require('./fixtures/testResponseAsync.json'), + testResponseJsonParams = require('./fixtures/testResponseJsonParams.json'), sanitize = require('../../lib/util').sanitize, sanitizeOptions = require('../../lib/util').sanitizeOptions; describe('csharp restsharp function', function () { describe('csharp-restsharp convert function', function () { - it('should return expected snippet', function () { - var request = new sdk.Request(mainCollection.item[4].request), + it('should return expected snippet - Async', function () { + var request = new Request(mainCollection.item[4].request), options = { indentCount: 1, indentType: 'Tab', @@ -25,13 +26,31 @@ describe('csharp restsharp function', function () { expect.fail(null, null, error); return; } - expect(snippet).deep.equal(testResponse.result); + expect(snippet).deep.equal(testResponseAsync.result); + }); + }); + + it('should return expected snippet json params', function () { + var request = new Request(mainCollection.item[5].request), + options = { + indentCount: 1, + indentType: 'Tab', + followRedirect: true, + trimRequestBody: true + }; + + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + return; + } + expect(snippet).deep.equal(testResponseJsonParams.result); }); }); }); describe('convert function', function () { - var request = new sdk.Request(testCollection.item[0].request), + var request = new Request(testCollection.item[0].request), snippetArray, options = { includeBoilerplate: true, @@ -45,7 +64,9 @@ describe('csharp restsharp function', function () { expect.fail(null, null, error); return; } - expect(snippet).to.include('using System;\nusing RestSharp;\nnamespace HelloWorldApplication {\n'); + expect(snippet).to.include('using System;\nusing RestSharp;\nusing System.Threading;\nusing' + + ' System.Threading.Tasks;\nnamespace HelloWorldApplication {\n'); + expect(snippet).to.include('static async Task Main(string[] args) {'); }); }); @@ -72,7 +93,7 @@ describe('csharp restsharp function', function () { expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.include('client.Timeout = 5'); + expect(snippet).to.include('MaxTimeout = 5'); }); }); @@ -82,12 +103,12 @@ describe('csharp restsharp function', function () { expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.include('client.FollowRedirects = false'); + expect(snippet).to.include('FollowRedirects = false'); }); }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -115,7 +136,7 @@ describe('csharp restsharp function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -165,9 +186,9 @@ describe('csharp restsharp function', function () { it('should use client.UserAgent instead of AddHeader function', function () { const sampleUA = 'Safari/605.1.15', - expectValue = `client.UserAgent = "${sampleUA}";`; + expectValue = `UserAgent = "${sampleUA}",`; - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -192,6 +213,7 @@ describe('csharp restsharp function', function () { expect(snippet).to.include(expectValue); }); }); + }); describe('getOptions function', function () { diff --git a/codegens/csharp-restsharp/test/unit/fixtures/testResponseAsync.json b/codegens/csharp-restsharp/test/unit/fixtures/testResponseAsync.json new file mode 100644 index 000000000..4c51c6eff --- /dev/null +++ b/codegens/csharp-restsharp/test/unit/fixtures/testResponseAsync.json @@ -0,0 +1,3 @@ +{ + "result" : "var options = new RestClientOptions(\"https://postman-echo.com\")\n{\n\tMaxTimeout = -1,\n};\nvar client = new RestClient(options);\nvar request = new RestRequest(\"/post/?hardik=\\\"me\\\"\", Method.Post);\nrequest.AddHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\nrequest.AddParameter(\"1\", \"a\");\nrequest.AddParameter(\"2\", \"b\");\nrequest.AddParameter(\"\\\"\\\"12\\\"\\\"\", \"\\\"23\\\"\");\nrequest.AddParameter(\"'1\\\"2\\\\\\\"\\\"3'\", \"'1\\\"23\\\"4'\");\nRestResponse response = await client.ExecuteAsync(request);\nConsole.WriteLine(response.Content);" +} diff --git a/codegens/csharp-restsharp/test/unit/fixtures/testResponseJsonParams.json b/codegens/csharp-restsharp/test/unit/fixtures/testResponseJsonParams.json new file mode 100644 index 000000000..8ea7f16d7 --- /dev/null +++ b/codegens/csharp-restsharp/test/unit/fixtures/testResponseJsonParams.json @@ -0,0 +1,3 @@ +{ + "result": "var options = new RestClientOptions(\"https://postman-echo.com\")\n{\n\tMaxTimeout = -1,\n};\nvar client = new RestClient(options);\nvar request = new RestRequest(\"/post\", Method.Post);\nrequest.AddHeader(\"Content-Type\", \"application/json\");\nvar body = @\"{\" + \"\\n\" +\n@\" \"\"json\"\": \"\"Test-Test\"\"\" + \"\\n\" +\n@\"}\";\nrequest.AddStringBody(body, DataFormat.Json);\nRestResponse response = await client.ExecuteAsync(request);\nConsole.WriteLine(response.Content);" +} diff --git a/codegens/csharp-restsharp/test/unit/fixtures/testcollection/collection.json b/codegens/csharp-restsharp/test/unit/fixtures/testcollection/collection.json index ed7f40ab5..0a945dc9e 100644 --- a/codegens/csharp-restsharp/test/unit/fixtures/testcollection/collection.json +++ b/codegens/csharp-restsharp/test/unit/fixtures/testcollection/collection.json @@ -1098,13 +1098,11 @@ "raw": "" }, "url": { - "raw": "https://704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1.mock.pstmn.io/link", + "raw": "https://postman-echo.com/link", "protocol": "https", "host": [ - "704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "link" @@ -1128,13 +1126,11 @@ "raw": "" }, "url": { - "raw": "https://704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1.mock.pstmn.io/unlick", + "raw": "https://postman-echo.com/unlick", "protocol": "https", "host": [ - "704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "unlick" @@ -1158,13 +1154,11 @@ "raw": "" }, "url": { - "raw": "https://704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1.mock.pstmn.io/lock", + "raw": "https://postman-echo.com/lock", "protocol": "https", "host": [ - "704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "lock" @@ -1180,13 +1174,11 @@ "header": [], "body": {}, "url": { - "raw": "https://704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1.mock.pstmn.io/unlock", + "raw": "https://postman-echo.com/unlock", "protocol": "https", "host": [ - "704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "unlock" @@ -1210,13 +1202,11 @@ "raw": "" }, "url": { - "raw": "https://704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1.mock.pstmn.io/profind", + "raw": "https://postman-echo.com/profind", "protocol": "https", "host": [ - "704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "profind" @@ -1240,13 +1230,11 @@ "raw": "" }, "url": { - "raw": "https://704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1.mock.pstmn.io/view", + "raw": "https://postman-echo.com/view", "protocol": "https", "host": [ - "704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "view" @@ -1262,13 +1250,11 @@ "header": [], "body": {}, "url": { - "raw": "https://704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1.mock.pstmn.io/view", + "raw": "https://postman-echo.com/view", "protocol": "https", "host": [ - "704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "view" @@ -1284,13 +1270,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1378,7 +1362,7 @@ } ], "cookie": [], - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1389,13 +1373,11 @@ "header": [], "body": {}, "url": { - "raw": "https://704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1.mock.pstmn.io/copy", + "raw": "https://postman-echo.com/copy", "protocol": "https", "host": [ - "704c30e8-77fe-4dc4-93e2-9c9c68dfb4e1", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "copy" @@ -1411,11 +1393,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1527,14 +1509,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/csharp-restsharp/test/unit/fixtures/testresponse.json b/codegens/csharp-restsharp/test/unit/fixtures/testresponse.json deleted file mode 100644 index 1e71bd241..000000000 --- a/codegens/csharp-restsharp/test/unit/fixtures/testresponse.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "result": "var client = new RestClient(\"https://postman-echo.com/post/?hardik=\\\"me\\\"\");\nclient.Timeout = -1;\nvar request = new RestRequest(Method.POST);\nrequest.AddHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\nrequest.AddParameter(\"1\", \"a\");\nrequest.AddParameter(\"2\", \"b\");\nrequest.AddParameter(\"\\\"\\\"12\\\"\\\"\", \"\\\"23\\\"\");\nrequest.AddParameter(\"'1\\\"2\\\\\\\"\\\"3'\", \"'1\\\"23\\\"4'\");\nIRestResponse response = client.Execute(request);\nConsole.WriteLine(response.Content);" -} \ No newline at end of file diff --git a/codegens/curl/.gitignore b/codegens/curl/.gitignore index 0b735f7ab..bfb38cf93 100644 --- a/codegens/curl/.gitignore +++ b/codegens/curl/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/curl/lib/index.js b/codegens/curl/lib/index.js index 253ffedc8..af57ca4d3 100644 --- a/codegens/curl/lib/index.js +++ b/codegens/curl/lib/index.js @@ -1,10 +1,15 @@ -var sanitize = require('./util').sanitize, - sanitizeOptions = require('./util').sanitizeOptions, - getUrlStringfromUrlObject = require('./util').getUrlStringfromUrlObject, - addFormParam = require('./util').addFormParam, - form = require('./util').form, - _ = require('./lodash'), - self; +const { + sanitize, + sanitizeOptions, + getUrlStringfromUrlObject, + getNtlmAuthInfo, + addFormParam, + form, + shouldAddHttpMethod + } = require('./util'), + _ = require('./lodash'); + +var self; self = module.exports = { convert: function (request, options, callback) { @@ -15,7 +20,7 @@ self = module.exports = { options = sanitizeOptions(options, self.getOptions()); var indent, trim, headersData, body, redirect, timeout, multiLine, - format, snippet, silent, url, quoteType; + format, snippet, silent, url, quoteType, ntlmAuth; redirect = options.followRedirect; timeout = options.requestTimeoutInSeconds; @@ -25,9 +30,16 @@ self = module.exports = { silent = options.silent; quoteType = options.quoteType === 'single' ? '\'' : '"'; url = getUrlStringfromUrlObject(request.url, quoteType); + ntlmAuth = getNtlmAuthInfo(request.auth, quoteType, format); - snippet = silent ? `curl ${form('-s', format)}` : 'curl'; + snippet = 'curl'; + if (ntlmAuth) { + snippet += ntlmAuth; + } + if (silent) { + snippet += ` ${form('-s', format)}`; + } if (redirect) { snippet += ` ${form('-L', format)}`; } @@ -35,7 +47,7 @@ self = module.exports = { snippet += ` ${form('-m', format)} ${timeout}`; } if ((url.match(/[{[}\]]/g) || []).length > 0) { - snippet += ' -g'; + snippet += ` ${form('-g', format)}`; } if (multiLine) { indent = options.indentType === 'Tab' ? '\t' : ' '; @@ -44,12 +56,14 @@ self = module.exports = { else { indent = ' '; } + if (request.method === 'HEAD') { - snippet += ` ${form('-I', format)} ${quoteType + url + quoteType}`; + snippet += ` ${form('-I', format)}`; } - else { - snippet += ` ${form('-X', format)} ${request.method} ${quoteType + url + quoteType}`; + if (shouldAddHttpMethod(request, options)) { + snippet += ` ${form('-X', format)} ${request.method}`; } + snippet += ` ${quoteType + url + quoteType}`; if (request.body && !request.headers.has('Content-Type')) { if (request.body.mode === 'file') { @@ -127,33 +141,55 @@ self = module.exports = { case 'urlencoded': _.forEach(body.urlencoded, function (data) { if (!data.disabled) { - // Using the long form below without considering the longFormat option, - // to generate more accurate and correct snippet - snippet += indent + '--data-urlencode'; - snippet += ` ${quoteType}${sanitize(data.key, trim, quoteType)}=` + - `${sanitize(data.value, trim, quoteType)}${quoteType}`; + snippet += indent + (format ? '--data-urlencode' : '-d'); + snippet += ` ${quoteType}${sanitize(data.key, trim, quoteType, false, true)}=` + + `${sanitize(data.value, trim, quoteType, false, !format)}${quoteType}`; } }); break; - case 'raw': - snippet += indent + `--data-raw ${quoteType}${sanitize(body.raw.toString(), trim, quoteType)}${quoteType}`; + case 'raw': { + let rawBody = body.raw.toString(), + isAsperandPresent = _.includes(rawBody, '@'), + // Use the long option if `@` is present in the request body otherwise follow user setting + optionName = isAsperandPresent ? '--data-raw' : form('-d', format), + sanitizedBody = sanitize(rawBody, trim, quoteType); + + if (!multiLine) { + try { + sanitizedBody = JSON.stringify(JSON.parse(sanitizedBody)); + } + catch (e) { + // Do nothing + } + } + + snippet += indent + `${optionName} ${quoteType}${sanitizedBody}${quoteType}`; + break; + } - case 'graphql': + case 'graphql': { // eslint-disable-next-line no-case-declarations let query = body.graphql ? body.graphql.query : '', - graphqlVariables; + graphqlVariables, requestBody, isAsperandPresent, optionName; try { graphqlVariables = JSON.parse(body.graphql.variables); } catch (e) { graphqlVariables = {}; } - snippet += indent + `--data-raw ${quoteType}${sanitize(JSON.stringify({ + + requestBody = JSON.stringify({ query: query, variables: graphqlVariables - }), trim, quoteType)}${quoteType}`; + }); + + isAsperandPresent = _.includes(requestBody, '@'); + // Use the long option if `@` is present in the request body otherwise follow user setting + optionName = isAsperandPresent ? '--data-raw' : form('-d', format); + snippet += indent + `${optionName} ${quoteType}${sanitize(requestBody, trim, quoteType)}${quoteType}`; break; + } case 'formdata': _.forEach(body.formdata, function (data) { if (!(data.disabled)) { @@ -176,7 +212,7 @@ self = module.exports = { }); break; case 'file': - snippet += indent + '--data-binary'; + snippet += indent + (format ? '--data-binary' : '-d'); snippet += ` ${quoteType}@${sanitize(body[body.mode].src, trim)}${quoteType}`; break; default: @@ -236,6 +272,13 @@ self = module.exports = { default: true, description: 'Automatically follow HTTP redirects' }, + { + name: 'Follow original HTTP method', + id: 'followOriginalHttpMethod', + type: 'boolean', + default: false, + description: 'Redirect with the original HTTP method instead of the default behavior of redirecting with GET' + }, { name: 'Trim request body fields', id: 'trimRequestBody', diff --git a/codegens/curl/lib/util.js b/codegens/curl/lib/util.js index 8346fb59e..a3a5d4020 100644 --- a/codegens/curl/lib/util.js +++ b/codegens/curl/lib/util.js @@ -1,3 +1,5 @@ +const _ = require('./lodash'); + var self = module.exports = { /** * sanitizes input string by handling escape characters eg: converts '''' to '\'\'', (" to \" and \ to \\ ) @@ -7,13 +9,18 @@ var self = module.exports = { * @param {Boolean} [trim] - indicates whether to trim string or not * @param {String} [quoteType] - indicates which quoteType has to be escaped * @param {Boolean} [backSlash] - indicates whether to escape backslash(\\) + * @param {Boolean} [urlEncode] - indicates whether to url-encode inputString * @returns {String} */ - sanitize: function (inputString, trim, quoteType, backSlash) { + sanitize: function (inputString, trim, quoteType, backSlash = false, urlEncode = false) { if (typeof inputString !== 'string') { return ''; } + if (urlEncode) { + inputString = encodeURIComponent(inputString); + } + if (backSlash) { inputString = inputString.replace(/\\/g, '\\\\'); } @@ -22,6 +29,13 @@ var self = module.exports = { inputString = inputString.replace(/"/g, '\\"'); // Escape backslash if double quote was already escaped before call to sanitize inputString = inputString.replace(/(? { + if (!param.disabled) { + memberCount += 1; + } + }); + + return memberCount === 0; + } + + return false; + }, + + /** + * Decide whether we should add the HTTP method explicitly to the cURL command. + * + * @param {Object} request + * @param {Object} options + * + * @returns {Boolean} + */ + shouldAddHttpMethod: function (request, options) { + let followRedirect = options.followRedirect, + followOriginalHttpMethod = options.followOriginalHttpMethod, + disableBodyPruning = true, + isBodyEmpty = self.isBodyEmpty(request.body); + + // eslint-disable-next-line lodash/prefer-is-nil + if (request.protocolProfileBehavior !== null && request.protocolProfileBehavior !== undefined) { + followRedirect = _.get(request, 'protocolProfileBehavior.followRedirects', followRedirect); + followOriginalHttpMethod = + _.get(request, 'protocolProfileBehavior.followOriginalHttpMethod', followOriginalHttpMethod); + disableBodyPruning = _.get(request, 'protocolProfileBehavior.disableBodyPruning', true); + } + + if (followRedirect && followOriginalHttpMethod) { + return true; + } + + switch (request.method) { + case 'HEAD': + return false; + case 'GET': + // disableBodyPruning will generally not be present in the request + // the only time it will be present, its value will be _false_ + // i.e. the user wants to prune the request body despite it being present + if (!isBodyEmpty && disableBodyPruning) { + return true; + } + + return false; + case 'POST': + return isBodyEmpty; + case 'DELETE': + case 'PUT': + case 'PATCH': + default: + return true; + } } }; diff --git a/codegens/curl/test/ci-install.sh b/codegens/curl/test/ci-install.sh new file mode 100755 index 000000000..881fb5bac --- /dev/null +++ b/codegens/curl/test/ci-install.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing curl" +sudo apt-get install -y curl diff --git a/codegens/curl/test/newman/newman.test.js b/codegens/curl/test/newman/newman.test.js index d3a943bf4..3cf1fb9f5 100644 --- a/codegens/curl/test/newman/newman.test.js +++ b/codegens/curl/test/newman/newman.test.js @@ -3,7 +3,7 @@ var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').ru describe('Convert for different types of request', function () { var testConfig = {compileScript: null, runScript: null, fileName: null}, - options = { + options1 = { indentCount: 3, indentType: 'Space', requestTimeout: 200, @@ -13,7 +13,19 @@ describe('Convert for different types of request', function () { silent: true, lineContinuationCharacter: '\\', quoteType: 'single' + }, + options2 = { + indentCount: 3, + indentType: 'Space', + requestTimeout: 200, + multiLine: true, + followRedirect: true, + longFormat: false, + silent: true, + lineContinuationCharacter: '\\', + quoteType: 'single' }; - runNewmanTest(convert, options, testConfig); + runNewmanTest(convert, options1, testConfig); + runNewmanTest(convert, options2, testConfig); }); diff --git a/codegens/curl/test/unit/convert.test.js b/codegens/curl/test/unit/convert.test.js index e54eff73c..d3c4c5061 100644 --- a/codegens/curl/test/unit/convert.test.js +++ b/codegens/curl/test/unit/convert.test.js @@ -1,5 +1,7 @@ -var expect = require('chai').expect, - sdk = require('postman-collection'), +var _ = require('lodash'), + expect = require('chai').expect, + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), convert = require('../../index').convert, getUrlStringfromUrlObject = require('../../lib/util').getUrlStringfromUrlObject; @@ -9,7 +11,7 @@ describe('curl convert function', function () { it('should return snippet with carat(^) as line continuation ' + 'character for multiline code generation', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -35,7 +37,7 @@ describe('curl convert function', function () { }); it('should return snippet with url in single quote(\')', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -57,7 +59,7 @@ describe('curl convert function', function () { }); it('should return snippet with url in double quote(")', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -79,7 +81,7 @@ describe('curl convert function', function () { }); it('should add semicolon after header key, if the value is empty string', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'GET', 'header': [ { @@ -110,7 +112,7 @@ describe('curl convert function', function () { it('should return snippet with backslash(\\) as line continuation ' + 'character for multiline code generation by default', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -136,7 +138,7 @@ describe('curl convert function', function () { it('should return snippet with backtick(`) as line continuation ' + 'character for multiline code generation', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -162,7 +164,7 @@ describe('curl convert function', function () { }); it('should add content type if formdata field contains a content-type', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -198,7 +200,7 @@ describe('curl convert function', function () { }); it('should parse header with string value properly', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [ { @@ -230,7 +232,7 @@ describe('curl convert function', function () { ']world', 'world}' ].forEach(function (value) { - request = new sdk.Request({ + const request = new Request({ 'method': 'GET', 'url': { 'raw': `http://example.com?hello=${value}`, @@ -253,11 +255,17 @@ describe('curl convert function', function () { } expect(snippet).to.include('-g'); }); + convert(request, { longFormat: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.include('--globoff'); + }); }); }); it('should return snippet without errors when request object has no body property', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -277,13 +285,57 @@ describe('curl convert function', function () { expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.include("GET 'https://google.com'"); // eslint-disable-line quotes + expect(snippet).to.include("'https://google.com'"); // eslint-disable-line quotes + }); + }); + + it('should return snippet with JSON body in single line if multiline option is false', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '{\n "name": "John",\n "type": "names",\n "id": "123sdaw"\n}', + 'options': { + 'raw': { + 'language': 'json' + } + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + multiLine: false, + longFormat: false, + lineContinuationCharacter: '\\', + quoteType: 'single', + requestTimeoutInSeconds: 0, + followRedirect: true, + followOriginalHttpMethod: false + }; + + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('-d \'{"name":"John","type":"names","id":"123sdaw"}\''); }); }); it('should return snippet with backslash(\\) character as line continuation ' + 'character for multiline code generation', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -310,7 +362,7 @@ describe('curl convert function', function () { it('should not encode queryParam unresolved variables and ' + 'leave it inside double parenthesis {{xyz}}', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'url': { @@ -343,7 +395,7 @@ describe('curl convert function', function () { }); it('should encode queryParams other than unresolved variables', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'url': { @@ -376,7 +428,7 @@ describe('curl convert function', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -405,7 +457,7 @@ describe('curl convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -455,7 +507,7 @@ describe('curl convert function', function () { it('should generate valid snippets for single/double quotes in URL', function () { // url = https://a"b'c.com/'d/"e - var request = new sdk.Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes + var request = new Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes convert(request, {}, function (error, snippet) { if (error) { expect.fail(null, null, error); @@ -468,7 +520,7 @@ describe('curl convert function', function () { it('should generate valid snippets when quoteType is "double"', function () { // url = https://a"b'c.com/'d/"e - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -500,7 +552,7 @@ describe('curl convert function', function () { }); it('should not add appropriate escaping characters when quote type is "double"', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -531,12 +583,76 @@ describe('curl convert function', function () { }); }); + it('should escape special characters when quoteType is "double"', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '{\r\n "hello": "$(whoami)"\r\n}', + 'options': { + 'raw': { + 'language': 'json' + } + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, { quoteType: 'double', lineContinuationCharacter: '^' }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet.includes('\\"hello\\": \\"\\$(whoami)\\"')).to.be.true; // eslint-disable-line + }); + }); + + it('should longer option for body even if longFormat is disabled if @ character is present', function () { + let request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '@hello' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { longFormat: false }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).include('--data-raw'); + }); + }); + describe('getUrlStringfromUrlObject function', function () { var rawUrl, urlObject, outputUrlString; it('should return empty string for an url object for an empty url or if no url object is passed', function () { rawUrl = ''; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.be.empty; outputUrlString = getUrlStringfromUrlObject(); @@ -545,14 +661,14 @@ describe('curl convert function', function () { it('should add protocol if present in the url object', function () { rawUrl = 'https://postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add the auth information if present in the url object', function () { rawUrl = 'https://user:password@postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); @@ -560,28 +676,28 @@ describe('curl convert function', function () { it('should not add the auth information if user isn\'t present but' + ' password is present in the url object', function () { rawUrl = 'https://:password@postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include(':password'); }); it('should add host if present in the url object', function () { rawUrl = 'https://postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add port if present in the url object', function () { rawUrl = 'https://postman-echo.com:8080'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add path if present in the url object', function () { rawUrl = 'https://postman-echo.com/get'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); @@ -590,7 +706,7 @@ describe('curl convert function', function () { it('should not encode unresolved query params', function () { rawUrl = 'https://postman-echo.com/get?key={{value}}'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include('key=%7B%7Bvalue%7B%7B'); expect(outputUrlString).to.equal(rawUrl); @@ -598,7 +714,7 @@ describe('curl convert function', function () { it('should encode query params other than unresolved variables', function () { rawUrl = 'https://postman-echo.com/get?key=\'a b c\''; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include('key=\'a b c\''); expect(outputUrlString).to.equal('https://postman-echo.com/get?key=%27a%20b%20c%27'); @@ -606,16 +722,23 @@ describe('curl convert function', function () { it('should not encode unresolved query params and ' + 'encode every other query param, both present together', function () { - rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b c\''; - urlObject = new sdk.Url(rawUrl); + rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\''; + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); - expect(outputUrlString).to.not.include('key2=\'a b c\''); - expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b%20c%27'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b+c%27'); + }); + + it('should not encode query params that are already encoded', function () { + rawUrl = 'https://postman-echo.com/get?query=urn%3Ali%3Afoo%3A62324'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal('https://postman-echo.com/get?query=urn%3Ali%3Afoo%3A62324'); }); it('should discard disabled query params', function () { - urlObject = new sdk.Url({ + urlObject = new Url({ protocol: 'https', host: 'postman-echo.com', query: [ @@ -630,10 +753,472 @@ describe('curl convert function', function () { it('should add hash if present in the url object', function () { rawUrl = 'https://postmanm-echo.com/get#hash'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); }); + + it('should not add --request parameter in POST request if body is present', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--request POST'); + }); + }); + + it('should add --request parameter in POST request if body is not present', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--request POST'); + }); + }); + + it('should add --request parameter in GET request if body is present', function () { + var request = new Request({ + 'method': 'GET', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + + convert(request, { followRedirect: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--request GET'); + }); + }); + + it('should not add --request parameter in GET request if body is present ' + + 'but disableBodyPruning is false', function () { + const request = new Request({ + 'method': 'GET', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + + // this needs to be done here because protocolProfileBehavior is not in collections SDK + request.protocolProfileBehavior = { + disableBodyPruning: false + }; + + convert(request, { followRedirect: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--request GET'); + }); + }); + + describe('followRedirect and followOriginalHttpMethod', function () { + it('should add --request parameter when passed true via options', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: true, followOriginalHttpMethod: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--request POST'); + }); + }); + + it('should not add --request parameter when passed false via options', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: false, followOriginalHttpMethod: false }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--request POST'); + }); + }); + + it('should add --request parameter when passed false via options but true in request settings', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + // this needs to be done here because protocolProfileBehavior is not in collections SDK + request.protocolProfileBehavior = { + followRedirects: true, + followOriginalHttpMethod: true + }; + + convert(request, { followRedirect: false, followOriginalHttpMethod: false }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--request POST'); + }); + }); + + it('should not add --request parameter when passed true via options but false in request settings', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + // this needs to be done here because protocolProfileBehavior is not in collections SDK + request.protocolProfileBehavior = { + followRedirects: false, + followOriginalHttpMethod: false + }; + + convert(request, { followRedirect: true, followOriginalHttpMethod: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--request POST'); + }); + }); + + it('should work when protocolProfileBehavior is null in request settings', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + // this needs to be done here because protocolProfileBehavior is not in collections SDK + request.protocolProfileBehavior = null; + + convert(request, { followRedirect: true, followOriginalHttpMethod: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--request POST'); + }); + }); + }); + + describe('should correctly handle NTLM auth', function () { + const sampleRequest = { + 'method': 'POST', + 'header': [], + 'auth': { + 'type': 'ntlm', + 'ntlm': [] + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }; + + it('when no username or password is present', function () { + const request = new Request(sampleRequest); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--ntlm'); + }); + }); + + it('when empty username and password is present', function () { + const request = new Request(Object.assign({ auth: { + 'type': 'ntlm', + 'ntlm': [ + {key: 'username', value: ''}, + {key: 'password', value: ''} + ] + }}, sampleRequest)); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--ntlm'); + }); + }); + + it('when correct username and password is present with single quotes as option', function () { + const request = new Request(_.set(sampleRequest, 'auth.ntlm', [ + {key: 'username', value: 'joh\'n'}, + {key: 'password', value: 'tennesse"e'} + ])); + + convert(request, { quoteType: 'single' }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.equal('curl --ntlm --user \'joh\'\\\'\'n:tennesse"e\' --location' + + ' --request POST \'https://postman-echo.com/post\''); + }); + }); + + it('when correct username and password is present with double as option', function () { + const request = new Request(_.set(sampleRequest, 'auth.ntlm', [ + {key: 'username', value: 'joh\'n'}, + {key: 'password', value: 'tennesse"e'} + ])); + + convert(request, { quoteType: 'double' }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.equal('curl --ntlm --user "joh\'n:tennesse\\"e" --location' + + ' --request POST "https://postman-echo.com/post"'); + }); + }); + + it('when correct username and password is present with long format option disabled', function () { + const request = new Request(_.set(sampleRequest, 'auth.ntlm', [ + {key: 'username', value: 'joh\'n'}, + {key: 'password', value: 'tennesse"e'} + ])); + + convert(request, { longFormat: false }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.equal('curl --ntlm -u \'joh\'\\\'\'n:tennesse"e\' -L' + + ' -X POST \'https://postman-echo.com/post\''); + }); + }); + + it('when username and password is present with domain as well', function () { + const request = new Request(_.set(sampleRequest, 'auth.ntlm', [ + {key: 'username', value: 'joh\'n'}, + {key: 'password', value: 'tennesse"e'}, + {key: 'domain', value: 'radio'} + ])); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.equal('curl --ntlm --user \'radio\\joh\'\\\'\'n:tennesse"e\' --location' + + ' --request POST \'https://postman-echo.com/post\''); + }); + }); + }); + + it('should use --data-binary when request body type is binary', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'file', + 'file': { + 'src': 'file-path/collection123.json' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + + convert(request, { longFormat: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--data-binary \'@file-path/collection123.json\''); + }); + }); }); }); diff --git a/codegens/curl/test/unit/fixtures/testcollection/collection.json b/codegens/curl/test/unit/fixtures/testcollection/collection.json index 51cc05fb7..363346323 100644 --- a/codegens/curl/test/unit/fixtures/testcollection/collection.json +++ b/codegens/curl/test/unit/fixtures/testcollection/collection.json @@ -1021,11 +1021,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1050,11 +1050,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1079,11 +1079,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1100,11 +1100,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1115,7 +1115,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1129,11 +1129,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1158,11 +1158,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1179,13 +1179,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1199,13 +1197,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1294,7 +1290,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1305,11 +1301,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1326,11 +1322,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1442,14 +1438,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/dart-dio/.gitignore b/codegens/dart-dio/.gitignore new file mode 100644 index 000000000..ca2205e31 --- /dev/null +++ b/codegens/dart-dio/.gitignore @@ -0,0 +1,56 @@ +.DS_Store + +#Obj-c files +*.m +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + +# Coverage directory used by tools like istanbul +.coverage + +# node-waf configuration +.lock-wscript + + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ +/.idea/ +pubspec.lock +pubspec.yaml +.packages +snippet.dart +.dart_tool/ diff --git a/codegens/dart-dio/.npmignore b/codegens/dart-dio/.npmignore new file mode 100644 index 000000000..79ad2ba5f --- /dev/null +++ b/codegens/dart-dio/.npmignore @@ -0,0 +1,76 @@ +### NPM Specific: Disregard recursive project files +### =============================================== +/.editorconfig +/.gitmodules +/test + +### Borrowed from .gitignore +### ======================== + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +snippet.swift + +out/ diff --git a/codegens/dart-dio/README.md b/codegens/dart-dio/README.md new file mode 100644 index 000000000..579d735c5 --- /dev/null +++ b/codegens/dart-dio/README.md @@ -0,0 +1,42 @@ + +> Converts Postman-SDK Request into code snippet for Dart dio. + +#### Prerequisites +To run Code-Gen, ensure that you have NodeJS >= v12. A copy of the NodeJS installable can be downloaded from https://nodejs.org/en/download/package-manager. + +## Using the Module +The module will expose an object which will have property `convert` which is the function for converting the Postman-SDK request to swift code snippet. + +### convert function +Convert function takes three parameters + +* `request` - Postman-SDK Request Object + +* `options` - options is an object which hsa following properties + * `indentType` - String denoting type of indentation for code snippet. eg: 'Space', 'Tab' + * `indentCount` - The number of indentation characters to add per code level + * `trimRequestBody` - Whether or not request body fields should be trimmed + +* `callback` - callback function with first parameter as error and second parameter as string for code snippet + +##### Example: +```js +var request = new sdk.Request('www.google.com'), //using postman sdk to create request + options = { + indentCount: 3, + indentType: 'Space', + requestTimeout: 200, + trimRequestBody: true + }; +convert(request, options, function(error, snippet) { + if (error) { + // handle error + } + // handle snippet +}); +``` +### Guidelines for using generated snippet + +* Since Postman-SDK Request object doesn't provide complete path of the file, it needs to be manually inserted in case of uploading a file. + +* This module doesn't support cookies. \ No newline at end of file diff --git a/codegens/dart-dio/index.js b/codegens/dart-dio/index.js new file mode 100644 index 000000000..bb0a047c4 --- /dev/null +++ b/codegens/dart-dio/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/codegens/dart-dio/lib/index.js b/codegens/dart-dio/lib/index.js new file mode 100644 index 000000000..fef51cae9 --- /dev/null +++ b/codegens/dart-dio/lib/index.js @@ -0,0 +1,346 @@ +var _ = require('lodash'), + sanitizeOptions = require('./util').sanitizeOptions, + sanitize = require('./util').sanitize, + addFormParam = require('./util').addFormParam, + path = require('path'), + self; + +/** + * Parses Url encoded data + * + * @param {Object} body body data + * @param {String} indent indentation required for code snippet + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseUrlEncoded (body, indent, trim) { + var bodySnippet = 'var data = {', + enabledBodyList = _.reject(body, 'disabled'), + bodyDataMap; + if (!_.isEmpty(enabledBodyList)) { + bodyDataMap = _.map(enabledBodyList, function (value) { + return `${indent}'${sanitize(value.key, trim)}': '${sanitize(value.value, trim)}'`; + }); + bodySnippet += '\n' + bodyDataMap.join(',\n') + '\n'; + } + bodySnippet += '};'; + return bodySnippet; +} + +/** + * Parses Raw data + * + * @param {Object} body Raw body data + * @param {Boolean} trim indicates whether to trim string or not + * @param {String} contentType the content-type of request body + * @param {Integer} indentCount the number of space to use + */ +function parseRawBody (body, trim, contentType, indentCount) { + if (contentType && (contentType === 'application/json' || contentType.match(/\+json$/))) { + try { + let jsonBody = JSON.parse(body); + return `var data = json.encode(${JSON.stringify(jsonBody, null, indentCount)});`; + + } + catch (error) { + // Do nothing + } + } + return `var data = '''${sanitize(body, trim)}''';`; +} + +/** + * Parses GraphQL body + * + * @param {Object} body GraphQL body + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseGraphQLBody (body, trim) { + var bodySnippet = '', + query = body ? body.query : '', + graphqlVariables; + try { + graphqlVariables = JSON.parse(body.variables); + } + catch (e) { + graphqlVariables = {}; + } + + bodySnippet += `var data = '''${sanitize(JSON.stringify({ + query: query, + variables: graphqlVariables + }), trim)}''';\n`; + + return bodySnippet; +} + +/** + * Parses form data body from request + * + * @param {Object} body form data Body + * @param {String} indent indentation required for code snippet + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseFormData (body, indent, trim) { + let bodySnippet = '', + formDataArray = [], + formDataFileArray = [], + key, + value; + + if (_.isEmpty(body)) { + return bodySnippet; + } + + _.forEach(body, function (data) { + key = trim ? data.key.trim() : data.key; + value = trim ? data.value.trim() : data.value; + if (!data.disabled) { + if (data.type === 'file') { + var pathArray = data.src.split(path.sep), + fileName = pathArray[pathArray.length - 1]; + formDataFileArray.push(`await MultipartFile.fromFile('${data.src}', filename: '${sanitize(fileName, trim)}')`); + } + else { + formDataArray.push(`${indent}'${sanitize(key)}': '${sanitize(value, trim)}'`); + } + } + }); + + if (formDataArray.length > 0 || formDataFileArray.length > 0) { + bodySnippet += 'var data = FormData.fromMap({\n'; + if (formDataFileArray.length > 0) { + bodySnippet += `${indent}'files': [\n${indent}${indent}`; + bodySnippet += formDataFileArray.join(`,\n${indent}${indent}`); + bodySnippet += `\n${indent}],\n`; + } + bodySnippet += formDataArray.join(',\n'); + bodySnippet += '\n});\n'; + } + + return bodySnippet; +} + +/** + * Parses Body from the Request + * + * @param {Object} body body object from request. + * @param {String} indent indentation required for code snippet + * @param {Boolean} trim indicates whether to trim string or not + * @param {String} contentType the content-type of the request body + */ +function parseBody (body, indent, trim, contentType) { + if (!_.isEmpty(body)) { + switch (body.mode) { + case 'urlencoded': + return parseUrlEncoded(body.urlencoded, indent, trim); + case 'raw': + return parseRawBody(body.raw, trim, contentType, indent.length); + case 'formdata': + return parseFormData(body.formdata, indent, trim); + case 'graphql': + return parseGraphQLBody(body.graphql, trim); + case 'file': + return 'var data = r\'\';\n'; + default: + return ''; + } + } + return ''; +} + +/** + * Parses headers from the request. + * + * @param {Object} headersArray array containing headers + * @param {String} indent indentation required for code snippet + * @param {Boolean} trim indicates whether to trim string or not + */ +function parseHeaders (headersArray, indent, trim) { + var headerString = '', + headerDictionary = []; + if (_.isEmpty(headersArray)) { + return headerString; + } + + headerString += 'var headers = {\n'; + + _.forEach(headersArray, function (header) { + if (!header.disabled) { + headerDictionary.push(indent + '\'' + header.key + '\': \'' + sanitize(header.value, trim) + '\''); + } + }); + + headerString += headerDictionary.join(',\n'); + headerString += '\n};\n'; + + return headerString; +} + + +self = module.exports = { + convert: function (request, options, callback) { + var indent, + codeSnippet = '', + headerSnippet = '', + footerSnippet = '', + trim, + timeout, + followRedirect, + contentType; + options = sanitizeOptions(options, self.getOptions()); + + trim = options.trimRequestBody; + indent = options.indentType === 'Tab' ? '\t' : ' '; + indent = indent.repeat(options.indentCount); + timeout = options.requestTimeout; + followRedirect = options.followRedirect; + + if (!_.isFunction(callback)) { + throw new Error('Callback is not valid function'); + } + + if (request.body && !request.headers.has('Content-Type')) { + if (request.body.mode === 'file') { + request.addHeader({ + key: 'Content-Type', + value: 'text/plain' + }); + } + else if (request.body.mode === 'graphql') { + request.addHeader({ + key: 'Content-Type', + value: 'application/json' + }); + } + } + + contentType = request.headers.get('Content-Type'); + if (options.includeBoilerplate) { + headerSnippet = 'import \'dart:convert\';\n'; + headerSnippet += 'import \'package:dio/dio.dart\';\n\n'; + headerSnippet += 'void main() async {\n'; + footerSnippet = '}\n'; + } + + if (request.body && request.body.mode === 'formdata') { + let formdata = request.body.formdata, + formdataArray = []; + formdata.members.forEach((param) => { + let key = param.key, + type = param.type, + disabled = param.disabled, + contentType = param.contentType; + // check if type is file or text + if (type === 'file') { + // if src is not of type string we check for array(multiple files) + if (typeof param.src !== 'string') { + // if src is an array(not empty), iterate over it and add files as separate form fields + if (Array.isArray(param.src) && param.src.length) { + param.src.forEach((filePath) => { + addFormParam(formdataArray, key, param.type, filePath, disabled, contentType); + }); + } + // if src is not an array or string, or is an empty array, add a placeholder for file path(no files case) + else { + addFormParam(formdataArray, key, param.type, '/path/to/file', disabled, contentType); + } + } + // if src is string, directly add the param with src as filepath + else { + addFormParam(formdataArray, key, param.type, param.src, disabled, contentType); + } + } + // if type is text, directly add it to formdata array + else { + addFormParam(formdataArray, key, param.type, param.value, disabled, contentType); + } + }); + request.body.update({ + mode: 'formdata', + formdata: formdataArray + }); + } + + const headers = parseHeaders(request.headers.toJSON(), indent, trim), + requestBody = request.body ? request.body.toJSON() : {}, + body = parseBody(requestBody, indent, trim, contentType); + + codeSnippet += headers; + + if (body !== '') { + codeSnippet += body + '\n'; + } + + codeSnippet += 'var dio = Dio();\n'; + codeSnippet += 'var response = await dio.request(\n'; + codeSnippet += `${indent}'${request.url.toString()}',\n`; + codeSnippet += `${indent}options: Options(\n`; + codeSnippet += `${indent}${indent}method: '${request.method.toUpperCase()}',\n`; + codeSnippet += `${headers !== '' ? `${indent}${indent}headers: headers,\n` : ''}`; + codeSnippet += `${followRedirect ? '' : `${indent}${indent}followRedirects: false,\n`}`; + codeSnippet += `${timeout ? `${indent}${indent}receiveTimeout: ${timeout},\n` : ''}`; + codeSnippet += `${indent}),\n`; + codeSnippet += `${body !== '' ? `${indent}data: data,\n` : ''}`; + codeSnippet += ');'; + + codeSnippet += '\n\n'; + codeSnippet += 'if (response.statusCode == 200) {\n'; + codeSnippet += `${indent}print(json.encode(response.data));\n`; + codeSnippet += '}\nelse {\n'; + codeSnippet += `${indent}print(response.statusMessage);\n`; + codeSnippet += '}'; + + (options.includeBoilerplate) && + (codeSnippet = indent + codeSnippet.split('\n').join('\n' + indent) + '\n'); + + callback(null, headerSnippet + codeSnippet + footerSnippet); + }, + getOptions: function () { + return [ + { + name: 'Set indentation count', + id: 'indentCount', + type: 'positiveInteger', + default: 2, + description: 'Set the number of indentation characters to add per code level' + }, + { + name: 'Set indentation type', + id: 'indentType', + type: 'enum', + availableOptions: ['Tab', 'Space'], + default: 'Space', + description: 'Select the character used to indent lines of code' + }, + { + name: 'Set request timeout', + id: 'requestTimeout', + type: 'positiveInteger', + default: 0, + description: 'Set number of milliseconds the request should wait for a response' + + ' before timing out (use 0 for infinity)' + }, + { + name: 'Trim request body fields', + id: 'trimRequestBody', + type: 'boolean', + default: false, + description: 'Remove white space and additional lines that may affect the server\'s response' + }, + { + name: 'Include boilerplate', + id: 'includeBoilerplate', + type: 'boolean', + default: false, + description: 'Include class definition and import statements in snippet' + }, + { + name: 'Follow redirects', + id: 'followRedirect', + type: 'boolean', + default: true, + description: 'Automatically follow HTTP redirects' + } + ]; + } +}; diff --git a/codegens/dart-dio/lib/util.js b/codegens/dart-dio/lib/util.js new file mode 100644 index 000000000..4d1801f91 --- /dev/null +++ b/codegens/dart-dio/lib/util.js @@ -0,0 +1,123 @@ +module.exports = { + /** + * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' + * and trim input if required + * + * @param {String} inputString + * @param {Boolean} [trim] - indicates whether to trim string or not + * @returns {String} + */ + sanitize: function (inputString, trim) { + if (typeof inputString !== 'string') { + return ''; + } + inputString = inputString.replace(/\\/g, '\\\\') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t') + .replace(/'/g, '\\\'') + .replace(/\$/g, '\\$'); + return trim ? inputString.trim() : inputString; + + }, + + /** + * sanitizes input options + * + * @param {Object} options - Options provided by the user + * @param {Array} optionsArray - options array received from getOptions function + * + * @returns {Object} - Sanitized options object + */ + sanitizeOptions: function (options, optionsArray) { + var result = {}, + defaultOptions = {}, + id; + optionsArray.forEach((option) => { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + + for (id in options) { + if (options.hasOwnProperty(id)) { + if (defaultOptions[id] === undefined) { + continue; + } + switch (defaultOptions[id].type) { + case 'boolean': + if (typeof options[id] !== 'boolean') { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'positiveInteger': + if (typeof options[id] !== 'number' || options[id] < 0) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'enum': + if (!defaultOptions[id].availableOptions.includes(options[id])) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + default: + result[id] = options[id]; + } + } + } + + for (id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + if (result[id] === undefined) { + result[id] = defaultOptions[id].default; + } + } + } + return result; + }, + + /** + * + * @param {Array} array - form data array + * @param {String} key - key of form data param + * @param {String} type - type of form data param(file/text) + * @param {String} val - value/src property of form data param + * @param {String} disabled - Boolean denoting whether the param is disabled or not + * @param {String} contentType - content type header of the param + * + * Appends a single param to form data array + */ + addFormParam: function (array, key, type, val, disabled, contentType) { + if (type === 'file') { + array.push({ + key: key, + type: type, + src: val, + disabled: disabled, + contentType: contentType + }); + } + else { + array.push({ + key: key, + type: type, + value: val, + disabled: disabled, + contentType: contentType + }); + } + } +}; diff --git a/codegens/dart-dio/npm/test-lint.js b/codegens/dart-dio/npm/test-lint.js new file mode 100644 index 000000000..2f4db0cb8 --- /dev/null +++ b/codegens/dart-dio/npm/test-lint.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +var shell = require('shelljs'), + chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/dart-dio/npm/test-newman.js b/codegens/dart-dio/npm/test-newman.js new file mode 100644 index 000000000..0c8559a8e --- /dev/null +++ b/codegens/dart-dio/npm/test-newman.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'newman'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running newman tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/dart-dio/npm/test.js b/codegens/dart-dio/npm/test.js new file mode 100644 index 000000000..ee0be8b7d --- /dev/null +++ b/codegens/dart-dio/npm/test.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node +var chalk = require('chalk'), + exit = require('shelljs').exit, + prettyms = require('pretty-ms'), + startedAt = Date.now(), + name = require('../package.json').name; + +require('async').series([ + require('./test-lint'), + require('./test-newman') + // Add a separate folder for every new suite of tests + // require('./test-unit') + // require('./test-browser') + // require('./test-integration') +], function (code) { + // eslint-disable-next-line max-len + console.info(chalk[code ? 'red' : 'green'](`\n${name}: duration ${prettyms(Date.now() - startedAt)}\n${name}: ${code ? 'not ok' : 'ok'}!`)); + exit(code && (typeof code === 'number' ? code : 1) || 0); +}); diff --git a/codegens/dart-dio/package.json b/codegens/dart-dio/package.json new file mode 100644 index 000000000..2dd1dd22f --- /dev/null +++ b/codegens/dart-dio/package.json @@ -0,0 +1,36 @@ +{ + "name": "@postman/codegen-dart-dio", + "version": "0.0.1", + "description": "Converts Postman SDK Requests to Dart Dio code snippet", + "main": "index.js", + "com_postman_plugin": { + "type": "code_generator", + "lang": "Dart", + "variant": "dio", + "syntax_mode": "dart" + }, + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "node npm/test.js", + "test-lint": "node npm/test-lint.js", + "test-unit": "node npm/test-unit.js", + "test-newman": "node npm/test-newman.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "Postman Labs ", + "license": "Apache-2.0", + "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/dart-dio", + "dependencies": { + "lodash": "4.17.21" + }, + "devDependencies": {}, + "engines": { + "node": ">=12" + } +} diff --git a/codegens/dart-dio/test/ci-install.sh b/codegens/dart-dio/test/ci-install.sh new file mode 100755 index 000000000..f63b1d8e7 --- /dev/null +++ b/codegens/dart-dio/test/ci-install.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/dart-dio" +pushd ./codegens/dart-dio &>/dev/null; + wget -q https://storage.googleapis.com/dart-archive/channels/stable/release/3.0.4/linux_packages/dart_3.0.4-1_amd64.deb + sudo dpkg -i dart_3.0.4-1_amd64.deb + echo '''name: test +version: 1.0.0 +environment: + sdk: ^3.0.3 +dependencies: + dio: ^5.2.0 + ''' > pubspec.yaml + dart pub get +popd &>/dev/null; diff --git a/codegens/dart-dio/test/newman/newman.test.js b/codegens/dart-dio/test/newman/newman.test.js new file mode 100644 index 000000000..e8b238678 --- /dev/null +++ b/codegens/dart-dio/test/newman/newman.test.js @@ -0,0 +1,18 @@ +var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, + convert = require('../../lib/index').convert; + + +describe('Dart-dio Converter', function () { + describe('convert for different request types', function () { + var options = {indentCount: 2, indentType: 'Space', includeBoilerplate: true }, + testConfig = { + runScript: 'dart snippet.dart', + fileName: 'snippet.dart', + headerSnippet: '', + // http uses Map to store headers, so there is no way to + // keep multiple headers with the same key + skipCollections: ['sameNameHeadersCollection', 'unsupportedMethods'] + }; + runNewmanTest(convert, options, testConfig); + }); +}); diff --git a/codegens/dart-dio/test/unit/convert.test.js b/codegens/dart-dio/test/unit/convert.test.js new file mode 100644 index 000000000..7274a0b51 --- /dev/null +++ b/codegens/dart-dio/test/unit/convert.test.js @@ -0,0 +1,121 @@ +var convert = require('../../index').convert, + expect = require('chai').expect, + { Request } = require('postman-collection/lib/collection/request'); + +// Disable check with expected snippets as we now have proper newman tests +describe('Dart Converter', function () { + it('should add timeout if requestTimeout options is used', function () { + var request = new Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ], + 'body': { + 'mode': 'raw', + 'raw': '{\n "json": "Test-Test"\n}' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, {requestTimeout: 5000}, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('receiveTimeout: 50000'); + }); + }); + + it('should add code for followRedirects if given in the option', function () { + var request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ] + } + }); + convert(request, { followRedirect: false }, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('followRedirects: false;'); + }); + }); + + it('should add boilerplate if given in the option', function () { + var request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ] + } + }); + convert(request, { includeBoilerplate: true }, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('import \'package:dio/dio.dart\';'); + expect(snippet).to.contain('void main() async {'); + }); + }); + + it('should add correct indentation', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'hello', + 'value': 'world', + 'type': 'text' + } + ] + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, { includeBoilerplate: true, indentType: 'Tab' }, function (err, snippet) { + if (err) { + expect.fail(err); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('\t\t\'hello\': \'world\''); + }); + }); +}); diff --git a/codegens/dart-http/.gitignore b/codegens/dart-http/.gitignore index 34f4d897c..ca2205e31 100644 --- a/codegens/dart-http/.gitignore +++ b/codegens/dart-http/.gitignore @@ -9,6 +9,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Coverage directory used by tools like istanbul .coverage diff --git a/codegens/dart-http/test/ci-install.sh b/codegens/dart-http/test/ci-install.sh new file mode 100755 index 000000000..40f651496 --- /dev/null +++ b/codegens/dart-http/test/ci-install.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/dart-http" +pushd ./codegens/dart-http &>/dev/null; + wget -q https://storage.googleapis.com/dart-archive/channels/stable/release/3.0.4/linux_packages/dart_3.0.4-1_amd64.deb + sudo dpkg -i dart_3.0.4-1_amd64.deb + echo '''name: test +version: 1.0.0 +environment: + sdk: ^3.0.3 +dependencies: + http: ^1.0.0 +''' > pubspec.yaml + dart pub get +popd &>/dev/null; diff --git a/codegens/dart-http/test/newman/newman.test.js b/codegens/dart-http/test/newman/newman.test.js index 2b4e3e1ca..c137c0475 100644 --- a/codegens/dart-http/test/newman/newman.test.js +++ b/codegens/dart-http/test/newman/newman.test.js @@ -9,7 +9,7 @@ describe('Convert for different types of request', function () { headerSnippet: '', // http uses Map to store headers, so there is no way to // keep multiple headers with the same key - skipCollections: ['sameNameHeadersCollection'] + skipCollections: ['sameNameHeadersCollection', 'unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); diff --git a/codegens/dart-http/test/unit/convert.test.js b/codegens/dart-http/test/unit/convert.test.js index 05e943c10..3653b31b1 100644 --- a/codegens/dart-http/test/unit/convert.test.js +++ b/codegens/dart-http/test/unit/convert.test.js @@ -1,11 +1,11 @@ var convert = require('../../index').convert, expect = require('chai').expect, - sdk = require('postman-collection'); + { Request } = require('postman-collection/lib/collection/request'); // Disable check with expected snippets as we now have proper newman tests describe('Dart Converter', function () { it('should add timeout if requestTimeout options is used', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -40,7 +40,7 @@ describe('Dart Converter', function () { }); it('should use http.MultipartRequest for formdata requests', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -69,7 +69,7 @@ describe('Dart Converter', function () { }); it('should add code for followRedirects if given in the option', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -91,7 +91,7 @@ describe('Dart Converter', function () { }); it('should add boilerplate if given in the option', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -114,7 +114,7 @@ describe('Dart Converter', function () { }); it('should add correct indentation', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { diff --git a/codegens/golang/.gitignore b/codegens/golang/.gitignore index c4d4ee535..23523185d 100644 --- a/codegens/golang/.gitignore +++ b/codegens/golang/.gitignore @@ -12,6 +12,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/golang/lib/index.js b/codegens/golang/lib/index.js index 46d936c01..b9436889c 100644 --- a/codegens/golang/lib/index.js +++ b/codegens/golang/lib/index.js @@ -3,6 +3,7 @@ var _ = require('./lodash'), sanitizeMultiline = require('./util').sanitizeMultiline, sanitizeOptions = require('./util').sanitizeOptions, addFormParam = require('./util').addFormParam, + getUrlStringfromUrlObject = require('./util').getUrlStringfromUrlObject, isFile = false, self; @@ -237,13 +238,13 @@ self = module.exports = { } if (isFile) { codeSnippet += `${indent}"os"\n${indent}"path/filepath"\n`; - codeSnippet += `${indent}"io"\n`; + // Setting isFile as false for further calls to this function isFile = false; } - codeSnippet += `${indent}"net/http"\n${indent}"io/ioutil"\n)\n\n`; + codeSnippet += `${indent}"net/http"\n${indent}"io"\n)\n\n`; - codeSnippet += `func main() {\n\n${indent}url := "${encodeURI(request.url.toString())}"\n`; + codeSnippet += `func main() {\n\n${indent}url := "${getUrlStringfromUrlObject(request.url)}"\n`; codeSnippet += `${indent}method := "${request.method}"\n\n`; if (bodySnippet !== '') { @@ -296,7 +297,7 @@ self = module.exports = { responseSnippet = `${indent}res, err := client.Do(req)\n`; responseSnippet += `${indent}if err != nil {\n${indent.repeat(2)}fmt.Println(err)\n`; responseSnippet += `${indent.repeat(2)}return\n${indent}}\n`; - responseSnippet += `${indent}defer res.Body.Close()\n\n${indent}body, err := ioutil.ReadAll(res.Body)\n`; + responseSnippet += `${indent}defer res.Body.Close()\n\n${indent}body, err := io.ReadAll(res.Body)\n`; responseSnippet += `${indent}if err != nil {\n${indent.repeat(2)}fmt.Println(err)\n`; responseSnippet += `${indent.repeat(2)}return\n${indent}}\n`; responseSnippet += `${indent}fmt.Println(string(body))\n}`; diff --git a/codegens/golang/lib/util.js b/codegens/golang/lib/util.js index 3745c9bd7..fae5ac768 100644 --- a/codegens/golang/lib/util.js +++ b/codegens/golang/lib/util.js @@ -1,12 +1,14 @@ -module.exports = { +const _ = require('./lodash'); + +const self = module.exports = { /** - * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' - * and trim input if required - * - * @param {String} inputString - * @param {Boolean} [trim] - indicates whether to trim string or not - * @returns {String} - */ + * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' + * and trim input if required + * + * @param {String} inputString + * @param {Boolean} [trim] - indicates whether to trim string or not + * @returns {String} + */ sanitize: function (inputString, trim) { if (typeof inputString !== 'string') { return ''; @@ -20,13 +22,13 @@ module.exports = { }, /** - * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' - * and trim input if required - * - * @param {String} inputString - * @param {Boolean} [trim] - indicates whether to trim string or not - * @returns {String} - */ + * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' + * and trim input if required + * + * @param {String} inputString + * @param {Boolean} [trim] - indicates whether to trim string or not + * @returns {String} + */ sanitizeMultiline: function (inputString, trim) { if (typeof inputString !== 'string') { return ''; @@ -38,6 +40,91 @@ module.exports = { }, + /** + * + * @param {Object} urlObject The request sdk request.url object + * @returns {String} The final string after parsing all the parameters of the url including + * protocol, auth, host, port, path, query, hash + * This will be used because the url.toString() method returned the URL with non encoded query string + * and hence a manual call is made to getQueryString() method with encode option set as true. + */ + getUrlStringfromUrlObject: function (urlObject) { + var url = ''; + if (!urlObject) { + return url; + } + if (urlObject.protocol) { + url += (urlObject.protocol.endsWith('://') ? urlObject.protocol : urlObject.protocol + '://'); + } + if (urlObject.auth && urlObject.auth.user) { + url = url + ((urlObject.auth.password) ? + urlObject.auth.user + ':' + urlObject.auth.password : urlObject.auth.user) + '@'; + } + if (urlObject.host) { + url += urlObject.getHost(); + } + if (urlObject.port) { + url += ':' + urlObject.port.toString(); + } + if (urlObject.path) { + url += urlObject.getPath(); + } + if (urlObject.query && urlObject.query.count()) { + let queryString = self.getQueryString(urlObject); + queryString && (url += '?' + queryString); + } + if (urlObject.hash) { + url += '#' + urlObject.hash; + } + + return self.sanitize(url, false); + }, + + /** + * @param {Object} urlObject + * @returns {String} + */ + getQueryString: function (urlObject) { + let isFirstParam = true, + params = _.get(urlObject, 'query.members'), + result = ''; + if (Array.isArray(params)) { + result = _.reduce(params, function (result, param) { + if (param.disabled === true) { + return result; + } + + if (isFirstParam) { + isFirstParam = false; + } + else { + result += '&'; + } + + return result + self.encodeParam(param.key) + '=' + self.encodeParam(param.value); + }, result); + } + + return result; + }, + + /** + * Encode param except the following characters- [,{,},],%,+ + * + * @param {String} param + * @returns {String} + */ + encodeParam: function (param) { + return encodeURIComponent(param) + .replace(/%5B/g, '[') + .replace(/%7B/g, '{') + .replace(/%5D/g, ']') + .replace(/%7D/g, '}') + .replace(/%2B/g, '+') + .replace(/%25/g, '%') + .replace(/'/g, '%27'); + }, + /** * sanitizes input options * diff --git a/codegens/golang/test/unit/convert.test.js b/codegens/golang/test/unit/convert.test.js index 4859c85e6..1b6b489bb 100644 --- a/codegens/golang/test/unit/convert.test.js +++ b/codegens/golang/test/unit/convert.test.js @@ -1,13 +1,15 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), - convert = require('../../index').convert; + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), + convert = require('../../index').convert, + getUrlStringfromUrlObject = require('../../lib/util').getUrlStringfromUrlObject; describe('Golang convert function', function () { describe('Convert function', function () { var request, options; it('should return snippet without errors when request object has no body property', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -33,7 +35,7 @@ describe('Golang convert function', function () { }); it('should parse headers with string value properly', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [ { @@ -66,7 +68,7 @@ describe('Golang convert function', function () { }); it('should add content type if formdata field contains a content-type', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -105,7 +107,7 @@ describe('Golang convert function', function () { }); it('should add time converted to seconds when input is taken in milliseconds ', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -130,7 +132,7 @@ describe('Golang convert function', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -157,7 +159,7 @@ describe('Golang convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -207,7 +209,7 @@ describe('Golang convert function', function () { it('should add error handling code everytime an error is possible', function () { var requests = []; - requests.push(new sdk.Request({ + requests.push(new Request({ 'method': 'GET', 'header': [ { @@ -224,7 +226,7 @@ describe('Golang convert function', function () { ] } })); - requests.push(new sdk.Request({ + requests.push(new Request({ 'method': 'POST', 'header': [], 'body': { @@ -253,5 +255,15 @@ describe('Golang convert function', function () { }); }); }); + + it('should not encode unresolved query params and ' + + 'encode every other query param, both present together', function () { + let rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\'', + urlObject = new Url(rawUrl), + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b+c%27'); + }); }); }); diff --git a/codegens/golang/test/unit/fixtures/testcollection/collection.json b/codegens/golang/test/unit/fixtures/testcollection/collection.json index ce70467d9..39d341ccc 100644 --- a/codegens/golang/test/unit/fixtures/testcollection/collection.json +++ b/codegens/golang/test/unit/fixtures/testcollection/collection.json @@ -49,11 +49,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -97,11 +97,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request?test=123&anotherone=232", + "raw": "https://postman-echo.com/request?test=123&anotherone=232", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -171,11 +171,11 @@ "raw": "\"'Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, \"id\" \"se\\\"mper\" sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.'\"" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -342,11 +342,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -404,11 +404,11 @@ "raw": "{\n \"json\": \"Test-Test\"\n}" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -589,11 +589,11 @@ "raw": "var val = 6;\nconsole.log(val);" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -644,11 +644,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -699,11 +699,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -728,14 +728,13 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request/:action", + "raw": "https://postman-echo.com/:action", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ - "request", ":action" ], "variable": [ @@ -788,11 +787,11 @@ "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -841,11 +840,11 @@ "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -912,11 +911,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -964,11 +963,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -991,11 +990,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1020,11 +1019,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1049,11 +1048,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1078,11 +1077,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1099,11 +1098,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1114,7 +1113,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1128,11 +1127,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1157,11 +1156,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1178,13 +1177,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1198,13 +1195,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1293,7 +1288,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1304,11 +1299,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1325,11 +1320,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1441,14 +1436,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/http/.gitignore b/codegens/http/.gitignore index f92058a8b..85e29b198 100644 --- a/codegens/http/.gitignore +++ b/codegens/http/.gitignore @@ -12,6 +12,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/http/lib/code-http-converter.js b/codegens/http/lib/code-http-converter.js index 7227eaa26..a025ec527 100644 --- a/codegens/http/lib/code-http-converter.js +++ b/codegens/http/lib/code-http-converter.js @@ -1,6 +1,6 @@ let utils = require('./util'), _ = require('./lodash'), - sdk = require('postman-collection'); + { Url } = require('postman-collection/lib/collection/url'); /** * Used in order to get additional options for generation of C# code snippet (i.e. Include Boilerplate code) @@ -33,7 +33,7 @@ function convert (request, options, callback) { url, host, path, query, body, headers; options = utils.sanitizeOptions(options, getOptions()); - url = sdk.Url.parse(request.url.toString()); + url = Url.parse(request.url.toString()); host = url.host ? url.host.join('.') : ''; host += url.port ? ':' + url.port : ''; path = url.path ? '/' + url.path.join('/') : '/'; diff --git a/codegens/http/lib/util.js b/codegens/http/lib/util.js index faf6f49c6..73685c3fe 100644 --- a/codegens/http/lib/util.js +++ b/codegens/http/lib/util.js @@ -36,7 +36,7 @@ var contentTypeHeaderMap = { 'js': 'text/javascript', 'json': 'application/json', 'jsonld': 'application/ld+json', - 'mid': 'audip/midi', + 'mid': 'audio/midi', 'midi': 'audio/midi', 'mjs': 'text/javascript', 'mp3': 'audio/mpeg', @@ -213,11 +213,12 @@ function getBody (request, trimRequestBody) { return trimRequestBody ? requestBody.trim() : requestBody; case FORM_DATA: - requestBody += `${FORM_DATA_BOUNDARY}\n`; + requestBody += `--${FORM_DATA_BOUNDARY}\n`; /* istanbul ignore else */ if (!_.isEmpty(request.body[request.body.mode])) { - let properties = getMembersOfPropertyList(request.body[request.body.mode]); - _.forEach(properties, function (property) { + let properties = getMembersOfPropertyList(request.body[request.body.mode]), + numberOfProperties = properties.length; + _.forEach(properties, function (property, index) { /* istanbul ignore else */ if (property.type === 'text') { requestBody += 'Content-Disposition: form-data; name="'; @@ -242,7 +243,12 @@ function getBody (request, trimRequestBody) { } requestBody += '(data)\n'; } - requestBody += `${FORM_DATA_BOUNDARY}\n`; + if (index === numberOfProperties - 1) { + requestBody += `--${FORM_DATA_BOUNDARY}--\n`; + } + else { + requestBody += `--${FORM_DATA_BOUNDARY}\n`; + } }); } return trimRequestBody ? requestBody.trim() : requestBody; diff --git a/codegens/http/npm-shrinkwrap.json b/codegens/http/npm-shrinkwrap.json index 8c3144dd4..012296db7 100644 --- a/codegens/http/npm-shrinkwrap.json +++ b/codegens/http/npm-shrinkwrap.json @@ -4,357 +4,110 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } + "@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==" }, "charset": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==" }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz", - "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==" - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "faker": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.1.0.tgz", - "integrity": "sha512-RrWKFSSA/aNLP0g3o2WW1Zez7/MnMr7xkiZmoCfAGZmdkDQZ6l2KtuXHN5XjdvpRjDl8+3vf+Rrtl06Z352+Mw==" - }, "file-type": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==" }, "http-reasons": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", - "integrity": "sha1-qVPKZwB4Zp3eFCzomUAbnW6F07Q=" + "integrity": "sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==" }, "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "liquid-json": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", - "integrity": "sha1-kVWhgTbYprJhXl8W+aJEira1Duo=" + "integrity": "sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==" }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" - }, - "marked": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.0.tgz", - "integrity": "sha512-tiRxakgbNPBr301ihe/785NntvYyhxlqcL3YaC8CaxJQh7kiaEtrN9B/eK2I2943Yjkh5gw25chYFDQhOMCwMA==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.0.tgz", - "integrity": "sha1-4p+IkeKE14JwJG8AUNaDS9u+EzI=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.2.tgz", + "integrity": "sha512-Y5ERWVcyh3sby9Fx2U5F1yatiTFjNsqF5NltihTWI9QgNtr5o3dbCZdcKa1l2wyfhnwwoP9HGNxga7LqZLA6gw==", "requires": { "charset": "^1.0.0" } }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.44.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } + "mime-db": "1.52.0" } }, "postman-collection": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-3.6.8.tgz", - "integrity": "sha512-TNPaK2dpVRhttUFo/WN0ReopXEtuSQMktwcvwJbQ0z8K+5hftvyx2ia40xgg9qFl/Ra78qoNTUmLL1s3lRqLMg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-5.0.0.tgz", + "integrity": "sha512-1LK795Atv/ZX3jK1MCTx9KCBz0rAiIJJhTLqnJ4AsXLiLSqJuAH1w5jI1CQzHVLpPFg6E8Rl4tQIhF0eBgKNQQ==", "requires": { - "escape-html": "1.0.3", - "faker": "5.1.0", + "@faker-js/faker": "5.5.3", "file-type": "3.9.0", "http-reasons": "0.1.0", - "iconv-lite": "0.6.2", + "iconv-lite": "0.6.3", "liquid-json": "0.3.1", - "lodash": "4.17.20", - "marked": "1.2.0", - "mime-format": "2.0.0", - "mime-types": "2.1.27", - "postman-url-encoder": "3.0.0", - "sanitize-html": "1.20.1", - "semver": "7.3.2", - "uuid": "3.4.0" + "lodash": "4.17.21", + "mime-format": "2.0.2", + "mime-types": "2.1.35", + "postman-url-encoder": "3.0.6", + "semver": "7.7.1", + "uuid": "8.3.2" } }, "postman-url-encoder": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.0.tgz", - "integrity": "sha512-bk5wus5/5Ei9pbh+sQXaAxS5n4ZwiNAaIA8VBvRcXP6QyKcue2yF6xk1HqdtaZoH1G8+6509SVeOBojoFQ7nrA==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.6.tgz", + "integrity": "sha512-uOlnZW+4Cmpbfbuq02hdj1hSpcIFmQxlAwsO6dflwUIVpt9+1duYVxXv3ikf+wHrAO8Wy98uVKnnuR8R0Qpdng==", "requires": { - "punycode": "^2.1.1" + "punycode": "^2.3.1" } }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sanitize-html": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", - "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", - "requires": { - "chalk": "^2.4.1", - "htmlparser2": "^3.10.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.mergewith": "^4.6.1", - "postcss": "^7.0.5", - "srcset": "^1.0.0", - "xtend": "^4.0.1" - } - }, "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } } diff --git a/codegens/http/package.json b/codegens/http/package.json index 5b35245b7..1264eb0af 100644 --- a/codegens/http/package.json +++ b/codegens/http/package.json @@ -21,7 +21,7 @@ "license": "Apache-2.0", "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/http", "dependencies": { - "postman-collection": "3.6.8" + "postman-collection": "^5.0.0" }, "scripts": { "test": "node npm/test.js", diff --git a/codegens/http/test/resources/expected-http-messages.json b/codegens/http/test/resources/expected-http-messages.json index 38c8755ad..33258ed46 100644 --- a/codegens/http/test/resources/expected-http-messages.json +++ b/codegens/http/test/resources/expected-http-messages.json @@ -4,7 +4,7 @@ "GET /headers HTTP/1.1\nHost: postman-echo.com\nmy-sample-header: Lorem ipsum dolor sit amet\nTEST: @#$%^&*()\nmore: ,./';[]}{\":?><|\\\\", "GET /headers HTTP/1.1\nHost: postman-echo.com\nmy-sample-header: Lorem ipsum dolor sit amet\nnot-disabled-header: ENABLED", "GET /get?test=123&anotherone=232 HTTP/1.1\nHost: postman-echo.com", - "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Length: 586\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\n\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"pl\"\n\n'a'\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"qu\"\n\n\"b\"\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"hdjkljh\"\n\nc\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"sa\"\n\nd\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"Special\"\n\n!@#$%&*()^_+=`~ \n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"more\"\n\n,./';[]}{\":?><|\\\\\n----WebKitFormBoundary7MA4YWxkTrZu0gW\n", + "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Length: 602\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\n\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"pl\"\n\n'a'\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"qu\"\n\n\"b\"\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"hdjkljh\"\n\nc\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"sa\"\n\nd\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"Special\"\n\n!@#$%&*()^_+=`~ \n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"more\"\n\n,./';[]}{\":?><|\\\\\n------WebKitFormBoundary7MA4YWxkTrZu0gW--\n", "POST /post?a=!@$^*()_-`%26&b=,./';[]}{\":/?><|| HTTP/1.1\nHost: postman-echo.com", "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 284\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.!@#$%^&*()+POL:},'';,[;[;\n\n\n", "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 147\n\n1='a'&2=%22b%22&'3'=c&%224%22=d&Special=!%40%23%24%25%26*()%5E_%3D%60~%20%20%20%20&more=%2C.%2F'%3B%5B%5D%7D%7B%22%3A%3F%3E%3C%7C%5C%5C%20%20%20%20", @@ -17,22 +17,22 @@ "PATCH /patch HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 256\n\nCurabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.", "DELETE /delete HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 256\n\nDonec fermentum, nisi sed cursus eleifend, nulla tortor ultricies tellus, ut vehicula orci arcu ut velit. In volutpat egestas dapibus.\nMorbi condimentum vestibulum sapien. Etiam dignissim diam quis eros lobortis gravida vel lobortis est. Etiam gravida sed.", "OPTIONS /post HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", - "LINK /request HTTP/1.1\nHost: mockbin.org\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", - "UNLINK /request HTTP/1.1\nHost: mockbin.org\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", - "LOCK /request HTTP/1.1\nHost: mockbin.org\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", - "UNLOCK /request HTTP/1.1\nHost: mockbin.org", - "PROPFIND /request HTTP/1.1\nHost: mockbin.org\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", - "VIEW /request HTTP/1.1\nHost: mockbin.org\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", - "PURGE / HTTP/1.1\nHost: 9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", - "COPY / HTTP/1.1\nHost: 9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", - "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Length: 174\n\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"file\"; filename=\"file.txt\"\nContent-Type: text/plain\n\n(data)\n----WebKitFormBoundary7MA4YWxkTrZu0gW\n", + "LINK /request HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", + "UNLINK /request HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", + "LOCK /request HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", + "UNLOCK /request HTTP/1.1\nHost: postman-echo.com", + "PROPFIND /request HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", + "VIEW /request HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 256\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.", + "PURGE / HTTP/1.1\nHost: postman-echo.com", + "COPY / HTTP/1.1\nHost: postman-echo.com", + "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Length: 180\n\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"file\"; filename=\"file.txt\"\nContent-Type: text/plain\n\n(data)\n------WebKitFormBoundary7MA4YWxkTrZu0gW--\n", "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Type: text/plain\nContent-Length: 22\n\n\"\"", "GET / HTTP/1.1\nHost: localhost:5050", "GET /knockknock HTTP/1.1\nHost: localhost:5050" ], "trimmedResult": [ "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 281\n\nDuis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.!@#$%^&*()+POL:},'';,[;[;", - "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Length: 581\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\n\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"pl\"\n\n'a'\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"qu\"\n\n\"b\"\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"hdjkljh\"\n\nc\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"sa\"\n\nd\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"Special\"\n\n!@#$%&*()^_+=`~\n----WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"more\"\n\n,./';[]}{\":?><|\\\\\n----WebKitFormBoundary7MA4YWxkTrZu0gW", + "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Length: 597\nContent-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\n\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"pl\"\n\n'a'\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"qu\"\n\n\"b\"\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"hdjkljh\"\n\nc\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"sa\"\n\nd\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"Special\"\n\n!@#$%&*()^_+=`~\n------WebKitFormBoundary7MA4YWxkTrZu0gW\nContent-Disposition: form-data; name=\"more\"\n\n,./';[]}{\":?><|\\\\\n------WebKitFormBoundary7MA4YWxkTrZu0gW--", "POST /post HTTP/1.1\nHost: postman-echo.com\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 123\n\n1='a'&2=%22b%22&'3'=c&%224%22=d&Special=!%40%23%24%25%26*()%5E_%3D%60~&more=%2C.%2F'%3B%5B%5D%7D%7B%22%3A%3F%3E%3C%7C%5C%5C" ] diff --git a/codegens/http/test/resources/test-collection.json b/codegens/http/test/resources/test-collection.json index 2d49f05fd..702e50005 100644 --- a/codegens/http/test/resources/test-collection.json +++ b/codegens/http/test/resources/test-collection.json @@ -992,7 +992,7 @@ "mode": "raw", "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, - "url": "https://mockbin.org/request" + "url": "https://postman-echo.com/request" }, "response": [] }, @@ -1010,7 +1010,7 @@ "mode": "raw", "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, - "url": "https://mockbin.org/request" + "url": "https://postman-echo.com/request" }, "response": [] }, @@ -1028,7 +1028,7 @@ "mode": "raw", "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, - "url": "https://mockbin.org/request" + "url": "https://postman-echo.com/request" }, "response": [] }, @@ -1041,12 +1041,12 @@ "mode": "raw", "raw": "" }, - "url": "https://mockbin.org/request" + "url": "https://postman-echo.com/request" }, "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1059,7 +1059,7 @@ "mode": "raw", "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, - "url": "https://mockbin.org/request" + "url": "https://postman-echo.com/request" }, "response": [] }, @@ -1077,7 +1077,7 @@ "mode": "raw", "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, - "url": "https://mockbin.org/request" + "url": "https://postman-echo.com/request" }, "response": [] }, @@ -1090,7 +1090,7 @@ "mode": "raw", "raw": "" }, - "url": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io" + "url": "https://postman-echo.com" }, "response": [] }, @@ -1103,7 +1103,7 @@ "mode": "raw", "raw": "" }, - "url": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io" + "url": "https://postman-echo.com" }, "response": [ { @@ -1115,7 +1115,7 @@ "mode": "raw", "raw": "" }, - "url": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io" + "url": "https://postman-echo.com" }, "status": "OK", "code": 200, diff --git a/codegens/java-okhttp/.gitignore b/codegens/java-okhttp/.gitignore index 7bfcb1aa5..1d4c13274 100644 --- a/codegens/java-okhttp/.gitignore +++ b/codegens/java-okhttp/.gitignore @@ -10,6 +10,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/java-okhttp/lib/okhttp.js b/codegens/java-okhttp/lib/okhttp.js index ee2aec561..81b07d321 100644 --- a/codegens/java-okhttp/lib/okhttp.js +++ b/codegens/java-okhttp/lib/okhttp.js @@ -189,7 +189,7 @@ function convert (request, options, callback) { if (options.includeBoilerplate) { headerSnippet = 'import java.io.*;\n' + 'import okhttp3.*;\n' + - 'public class main {\n' + + 'public class Main {\n' + indentString + 'public static void main(String []args) throws IOException{\n'; footerSnippet = indentString.repeat(2) + 'System.out.println(response.body().string());\n' + indentString + '}\n}\n'; diff --git a/codegens/java-okhttp/test/ci-install.sh b/codegens/java-okhttp/test/ci-install.sh new file mode 100755 index 000000000..f1f1c04c0 --- /dev/null +++ b/codegens/java-okhttp/test/ci-install.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/java-okhttp" +pushd ./codegens/java-okhttp &>/dev/null; + sudo add-apt-repository ppa:openjdk-r/ppa -y + sudo rm -rf /var/lib/apt/lists/* + sudo apt-get update + sudo apt-get install -y openjdk-8-jdk + unzip test/unit/fixtures/dependencies.zip +popd &>/dev/null; diff --git a/codegens/java-okhttp/test/newman/newman.test.js b/codegens/java-okhttp/test/newman/newman.test.js index f2dc6fb3f..47deffd5b 100644 --- a/codegens/java-okhttp/test/newman/newman.test.js +++ b/codegens/java-okhttp/test/newman/newman.test.js @@ -4,9 +4,9 @@ var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').ru describe.skip('convert for different request types', function () { var options = {indentCount: 3, indentType: 'Space', includeBoilerplate: true}, testConfig = { - compileScript: 'javac -cp *: main.java', - runScript: 'java -cp *: main', - fileName: 'main.java', + compileScript: 'javac -cp *: Main.java', + runScript: 'java -cp *: Main', + fileName: 'Main.java', skipCollections: ['redirectCollection'] }; runNewmanTest(convert, options, testConfig); diff --git a/codegens/java-okhttp/test/unit/convert.test.js b/codegens/java-okhttp/test/unit/convert.test.js index f2206cb72..1406036a7 100644 --- a/codegens/java-okhttp/test/unit/convert.test.js +++ b/codegens/java-okhttp/test/unit/convert.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), sanitize = require('../../lib/util').sanitize, convert = require('../../lib/index').convert, getOptions = require('../../lib/index').getOptions, @@ -7,7 +7,7 @@ var expect = require('chai').expect, describe('okhttp convert function', function () { describe('convert function', function () { - var request = new sdk.Request(mainCollection.item[0].request), + var request = new Request(mainCollection.item[0].request), snippetArray, options = { includeBoilerplate: true, @@ -25,7 +25,7 @@ describe('okhttp convert function', function () { } snippetArray = snippet.split('\n'); for (var i = 0; i < snippetArray.length; i++) { - if (snippetArray[i].startsWith('public class main {')) { + if (snippetArray[i].startsWith('public class Main {')) { expect(snippetArray[i + 1].substr(0, 4)).to.equal(SINGLE_SPACE.repeat(4)); expect(snippetArray[i + 1].charAt(4)).to.not.equal(SINGLE_SPACE); } @@ -39,7 +39,7 @@ describe('okhttp convert function', function () { expect.fail(null, null, error); return; } - expect(snippet).to.include('import java.io.*;\nimport okhttp3.*;\npublic class main {\n'); + expect(snippet).to.include('import java.io.*;\nimport okhttp3.*;\npublic class Main {\n'); }); }); @@ -81,7 +81,7 @@ describe('okhttp convert function', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -108,7 +108,7 @@ describe('okhttp convert function', function () { }); it('should add content type if formdata field contains a content-type', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -143,7 +143,7 @@ describe('okhttp convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { diff --git a/codegens/java-unirest/.gitignore b/codegens/java-unirest/.gitignore index bbdb09d31..106dca989 100644 --- a/codegens/java-unirest/.gitignore +++ b/codegens/java-unirest/.gitignore @@ -13,6 +13,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/java-unirest/lib/parseRequest.js b/codegens/java-unirest/lib/parseRequest.js index df2bb11cc..1c16c66e2 100644 --- a/codegens/java-unirest/lib/parseRequest.js +++ b/codegens/java-unirest/lib/parseRequest.js @@ -2,6 +2,50 @@ var _ = require('./lodash'), sanitize = require('./util').sanitize; +/** + * Encode param except the following characters- [,],%,+ + * Characters { and } are kept encoded because unirest does not support them + * + * @param {String} param + * @returns {String} + */ +function encodeParam (param) { + return encodeURIComponent(param) + .replace(/%5B/g, '[') + .replace(/%5D/g, ']') + .replace(/%2B/g, '+') + .replace(/%25/g, '%') + .replace(/'/g, '%27'); +} + +/** + * @param {Object} urlObject + * @returns {String} + */ +function getQueryString (urlObject) { + let isFirstParam = true, + params = _.get(urlObject, 'query.members'), + result = ''; + if (Array.isArray(params)) { + result = _.reduce(params, function (result, param) { + if (param.disabled === true) { + return result; + } + + if (isFirstParam) { + isFirstParam = false; + } + else { + result += '&'; + } + + return result + encodeParam(param.key) + '=' + encodeParam(param.value); + }, result); + } + + return result; +} + /** * * @param {*} urlObject The request sdk request.url object @@ -33,7 +77,7 @@ function getUrlStringfromUrlObject (urlObject) { url += urlObject.getPath(); } if (urlObject.query && urlObject.query.count()) { - let queryString = urlObject.getQueryString({ ignoreDisabled: true, encode: true }); + let queryString = getQueryString(urlObject); queryString && (url += '?' + queryString); } if (urlObject.hash) { diff --git a/codegens/java-unirest/lib/unirest.js b/codegens/java-unirest/lib/unirest.js index f72043563..540aee882 100644 --- a/codegens/java-unirest/lib/unirest.js +++ b/codegens/java-unirest/lib/unirest.js @@ -186,7 +186,7 @@ function convert (request, options, callback) { if (options.includeBoilerplate) { headerSnippet = 'import com.mashape.unirest.http.*;\n' + 'import java.io.*;\n' + - 'public class main {\n' + + 'public class Main {\n' + indentString + 'public static void main(String []args) throws Exception{\n'; footerSnippet = indentString.repeat(2) + 'System.out.println(response.getBody());\n' + indentString + '}\n}\n'; diff --git a/codegens/java-unirest/test/ci-install.sh b/codegens/java-unirest/test/ci-install.sh new file mode 100755 index 000000000..d89197a6b --- /dev/null +++ b/codegens/java-unirest/test/ci-install.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/java-unirest" +pushd ./codegens/java-unirest &>/dev/null; + unzip test/unit/fixtures/dependencies.zip +popd &>/dev/null; diff --git a/codegens/java-unirest/test/newman/newman.test.js b/codegens/java-unirest/test/newman/newman.test.js index adafad2ac..5b8620407 100644 --- a/codegens/java-unirest/test/newman/newman.test.js +++ b/codegens/java-unirest/test/newman/newman.test.js @@ -3,10 +3,10 @@ var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').ru describe('Convert for different types of request', function () { var testConfig = { - runScript: 'java -cp *: main', - compileScript: 'javac -cp *: main.java', - fileName: 'main.java', - skipCollections: ['formdataCollection', 'emptyFormdataCollection'] + runScript: 'java -cp *: Main', + compileScript: 'javac -cp *: Main.java', + fileName: 'Main.java', + skipCollections: ['formdataCollection', 'emptyFormdataCollection', 'unsupportedMethods'] }, options = {includeBoilerplate: true}; diff --git a/codegens/java-unirest/test/unit/convert.test.js b/codegens/java-unirest/test/unit/convert.test.js index a400aff77..03e1e4c73 100644 --- a/codegens/java-unirest/test/unit/convert.test.js +++ b/codegens/java-unirest/test/unit/convert.test.js @@ -1,5 +1,6 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), convert = require('../../lib/index').convert, sanitize = require('../../lib/util').sanitize, getUrlStringfromUrlObject = require('../../lib/parseRequest').getUrlStringfromUrlObject, @@ -18,7 +19,7 @@ describe('java unirest convert function for test collection', function () { line_no; it('should return a Tab indented snippet ', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { indentType: 'Tab', indentCount: 1 @@ -43,7 +44,7 @@ describe('java unirest convert function for test collection', function () { }); it('should return snippet with setTimeouts function when timeout is set to non zero', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000 }; @@ -59,7 +60,7 @@ describe('java unirest convert function for test collection', function () { it('should return snippet with setTimeouts function setting both ' + 'connection and socket timeout to 0 when requestTimeout is set to 0', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 0 }; @@ -75,7 +76,7 @@ describe('java unirest convert function for test collection', function () { it('should return snippet with disableRedirectHandling function for' + 'follow redirect option set to false', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { followRedirect: false }; @@ -90,7 +91,7 @@ describe('java unirest convert function for test collection', function () { }); it('should add content type if formdata field contains a content-type', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -126,7 +127,7 @@ describe('java unirest convert function for test collection', function () { it('should include import statements, main class and print statements ' + 'when includeBoilerplate is set to true', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { includeBoilerplate: true, indentType: 'Tab', @@ -134,7 +135,7 @@ describe('java unirest convert function for test collection', function () { }; headerSnippet = 'import com.mashape.unirest.http.*;\n' + 'import java.io.*;\n' + - 'public class main {\n' + + 'public class Main {\n' + indentString + 'public static void main(String []args) throws Exception{\n'; footerSnippet = indentString.repeat(2) + 'System.out.println(response.getBody());\n' + indentString + '}\n}\n'; @@ -156,7 +157,7 @@ describe('java unirest convert function for test collection', function () { 'url': 'https://echo.getpostman.com/post', 'method': 'POST' }; - request = new sdk.Request(reqObject); + request = new Request(reqObject); options = {}; convert(request, options, function (error, snippet) { if (error) { @@ -174,10 +175,10 @@ describe('java unirest convert function for test collection', function () { 'six HTTP methods', function () { reqObject = { 'description': 'This is a sample PROPFIND request', - 'url': 'https://mockbin.org/request', + 'url': 'https://postman-echo.com/request', 'method': 'PROPFIND' }; - request = new sdk.Request(reqObject); + request = new Request(reqObject); options = {}; convert(request, options, function (error, snippet) { if (error) { @@ -192,7 +193,7 @@ describe('java unirest convert function for test collection', function () { it('should not encode queryParam unresolved variables and ' + 'leave it inside double parenthesis {{xyz}}', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'url': { @@ -218,13 +219,13 @@ describe('java unirest convert function for test collection', function () { expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.include('http://postman-echo.com/post?a={{xyz}}'); - expect(snippet).to.not.include('http://postman-echo.com/post?a=%7B%7Bxyz%7D%7D'); + expect(snippet).to.not.include('http://postman-echo.com/post?a={{xyz}}'); + expect(snippet).to.include('http://postman-echo.com/post?a=%7B%7Bxyz%7D%7D'); }); }); it('should encode queryParams other than unresolved variables', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'url': { @@ -256,7 +257,7 @@ describe('java unirest convert function for test collection', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -284,7 +285,7 @@ describe('java unirest convert function for test collection', function () { it('should add the force multipart body method when ' + 'there are no files but contains text field params in multipart/form-data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'body': { 'mode': 'formdata', @@ -302,10 +303,10 @@ describe('java unirest convert function for test collection', function () { ] }, 'url': { - 'raw': 'https://postman-echo/', + 'raw': 'https://postman-echo.com/', 'protocol': 'https', 'host': [ - 'google', + 'postman-echo', 'com' ] } @@ -323,7 +324,7 @@ describe('java unirest convert function for test collection', function () { it('should not add the force multipart body method when ' + 'there are file fields in multipart/form-data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'body': { 'mode': 'formdata', @@ -341,10 +342,10 @@ describe('java unirest convert function for test collection', function () { ] }, 'url': { - 'raw': 'https://postman-echo/', + 'raw': 'https://postman-echo.com/', 'protocol': 'https', 'host': [ - 'google', + 'postman-echo', 'com' ] } @@ -359,7 +360,7 @@ describe('java unirest convert function for test collection', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -407,7 +408,7 @@ describe('java unirest convert function for test collection', function () { it('should generate valid snippets for single/double quotes in URL', function () { // url = https://a"b'c.com/'d/"e - var request = new sdk.Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes + var request = new Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes convert(request, {}, function (error, snippet) { if (error) { expect.fail(null, null, error); @@ -423,7 +424,7 @@ describe('java unirest convert function for test collection', function () { it('should return empty string for an url object for an empty url or if no url object is passed', function () { rawUrl = ''; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.be.empty; outputUrlString = getUrlStringfromUrlObject(); @@ -432,14 +433,14 @@ describe('java unirest convert function for test collection', function () { it('should add protocol if present in the url object', function () { rawUrl = 'https://postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add the auth information if present in the url object', function () { rawUrl = 'https://user:password@postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); @@ -447,28 +448,28 @@ describe('java unirest convert function for test collection', function () { it('should not add the auth information if user isn\'t present but' + ' password is present in the url object', function () { rawUrl = 'https://:password@postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include(':password'); }); it('should add host if present in the url object', function () { rawUrl = 'https://postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add port if present in the url object', function () { rawUrl = 'https://postman-echo.com:8080'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add path if present in the url object', function () { rawUrl = 'https://postman-echo.com/get'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); @@ -477,32 +478,39 @@ describe('java unirest convert function for test collection', function () { it('should not encode unresolved query params', function () { rawUrl = 'https://postman-echo.com/get?key={{value}}'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); - expect(outputUrlString).to.not.include('key=%7B%7Bvalue%7B%7B'); - expect(outputUrlString).to.equal(rawUrl); + expect(outputUrlString).to.include('key=%7B%7Bvalue%7D%7D'); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key=%7B%7Bvalue%7D%7D'); }); it('should encode query params other than unresolved variables', function () { rawUrl = 'https://postman-echo.com/get?key=\'a b c\''; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include('key=\'a b c\''); expect(outputUrlString).to.equal('https://postman-echo.com/get?key=%27a%20b%20c%27'); }); + it('should not encode query params that are already encoded', function () { + rawUrl = 'https://postman-echo.com/get?query=urn%3Ali%3Afoo%3A62324'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal('https://postman-echo.com/get?query=urn%3Ali%3Afoo%3A62324'); + }); + it('should not encode unresolved query params and ' + 'encode every other query param, both present together', function () { - rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b c\''; - urlObject = new sdk.Url(rawUrl); + rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\''; + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); - expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); - expect(outputUrlString).to.not.include('key2=\'a b c\''); - expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b%20c%27'); + expect(outputUrlString).to.include('key1=%7B%7Bvalue%7D%7D'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1=%7B%7Bvalue%7D%7D&key2=%27a%20b+c%27'); }); it('should discard disabled query params', function () { - urlObject = new sdk.Url({ + urlObject = new Url({ protocol: 'https', host: 'postman-echo.com', query: [ @@ -517,7 +525,7 @@ describe('java unirest convert function for test collection', function () { it('should add hash if present in the url object', function () { rawUrl = 'https://postmanm-echo.com/get#hash'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); diff --git a/codegens/js-fetch/.gitignore b/codegens/js-fetch/.gitignore index cbf9ecbc5..ff20256ea 100644 --- a/codegens/js-fetch/.gitignore +++ b/codegens/js-fetch/.gitignore @@ -11,6 +11,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/js-fetch/README.md b/codegens/js-fetch/README.md index 2d592ae1d..f338dfe8f 100644 --- a/codegens/js-fetch/README.md +++ b/codegens/js-fetch/README.md @@ -19,6 +19,7 @@ Convert function takes three parameters * `trimRequestBody` - Trim request body fields * `followRedirect` - Boolean denoting whether to redirect a request * `requestTimeout` - Integer denoting time after which the request will bail out in milli-seconds + * `asyncAwaitEnabled` : Boolean denoting whether to use async/await syntax * `callback` - callback function with first parameter as error and second parameter as string for code snippet diff --git a/codegens/js-fetch/lib/index.js b/codegens/js-fetch/lib/index.js index d93c8b385..701e22db7 100644 --- a/codegens/js-fetch/lib/index.js +++ b/codegens/js-fetch/lib/index.js @@ -24,7 +24,7 @@ function redirectMode (redirect) { * @param {boolean} trim trim body option */ function parseURLEncodedBody (body, trim) { - var bodySnippet = 'var urlencoded = new URLSearchParams();\n'; + var bodySnippet = 'const urlencoded = new URLSearchParams();\n'; _.forEach(body, function (data) { if (!data.disabled) { bodySnippet += `urlencoded.append("${sanitize(data.key, trim)}", "${sanitize(data.value, trim)}");\n`; @@ -40,7 +40,7 @@ function parseURLEncodedBody (body, trim) { * @param {boolean} trim trim body option */ function parseFormData (body, trim) { - var bodySnippet = 'var formdata = new FormData();\n'; + var bodySnippet = 'const formdata = new FormData();\n'; _.forEach(body, function (data) { if (!data.disabled) { if (data.type === 'file') { @@ -65,7 +65,7 @@ function parseFormData (body, trim) { * @param {String} indentString Indentation string */ function parseRawBody (body, trim, contentType, indentString) { - var bodySnippet = 'var raw = '; + var bodySnippet = 'const raw = '; // Match any application type whose underlying structure is json // For example application/vnd.api+json // All of them have +json as suffix @@ -101,7 +101,7 @@ function parseGraphQL (body, trim, indentString) { catch (e) { graphqlVariables = {}; } - bodySnippet = 'var graphql = JSON.stringify({\n'; + bodySnippet = 'const graphql = JSON.stringify({\n'; bodySnippet += `${indentString}query: "${sanitize(query, trim)}",\n`; bodySnippet += `${indentString}variables: ${JSON.stringify(graphqlVariables)}\n})`; return bodySnippet; @@ -113,7 +113,7 @@ function parseGraphQL (body, trim, indentString) { * parses binamry file data */ function parseFileData () { - var bodySnippet = 'var file = "";\n'; + var bodySnippet = 'const file = "";\n'; return bodySnippet; } @@ -154,7 +154,7 @@ function parseBody (body, trim, indentString, contentType) { function parseHeaders (headers) { var headerSnippet = ''; if (!_.isEmpty(headers)) { - headerSnippet = 'var myHeaders = new Headers();\n'; + headerSnippet = 'const myHeaders = new Headers();\n'; headers = _.reject(headers, 'disabled'); _.forEach(headers, function (header) { headerSnippet += `myHeaders.append("${sanitize(header.key, true)}", "${sanitize(header.value)}");\n`; @@ -209,6 +209,13 @@ function getOptions () { type: 'boolean', default: false, description: 'Remove white space and additional lines that may affect the server\'s response' + }, + { + name: 'Use async/await', + id: 'asyncAwaitEnabled', + type: 'boolean', + default: false, + description: 'Modifies code snippet to use async/await' } ]; } @@ -238,7 +245,6 @@ function convert (request, options, callback) { headerSnippet = '', bodySnippet = '', optionsSnippet = '', - timeoutSnippet = '', fetchSnippet = ''; indent = indent.repeat(options.indentCount); if (request.body && request.body.mode === 'graphql' && !request.headers.has('Content-Type')) { @@ -294,8 +300,12 @@ function convert (request, options, callback) { body = request.body && request.body.toJSON(); bodySnippet = parseBody(body, trim, indent, request.headers.get('Content-Type')); - optionsSnippet = `var requestOptions = {\n${indent}`; - optionsSnippet += `method: '${request.method}',\n${indent}`; + if (options.requestTimeout > 0) { + codeSnippet += 'const controller = new AbortController();\n'; + codeSnippet += `const timerId = setTimeout(() => controller.abort(), ${options.requestTimeout});\n`; + } + optionsSnippet = `const requestOptions = {\n${indent}`; + optionsSnippet += `method: "${request.method}",\n${indent}`; if (headerSnippet !== '') { optionsSnippet += `headers: myHeaders,\n${indent}`; codeSnippet += headerSnippet + '\n'; @@ -305,30 +315,39 @@ function convert (request, options, callback) { optionsSnippet += `body: ${body.mode},\n${indent}`; codeSnippet += bodySnippet + '\n'; } - optionsSnippet += `redirect: '${redirectMode(options.followRedirect)}'\n};\n`; + if (options.requestTimeout > 0) { + optionsSnippet += `signal: controller.signal,\n${indent}`; + } + optionsSnippet += `redirect: "${redirectMode(options.followRedirect)}"\n};\n`; codeSnippet += optionsSnippet + '\n'; - fetchSnippet = `fetch("${sanitize(request.url.toString())}", requestOptions)\n${indent}`; - fetchSnippet += `.then(response => response.text())\n${indent}`; - fetchSnippet += `.then(result => console.log(result))\n${indent}`; - fetchSnippet += '.catch(error => console.log(\'error\', error));'; - - if (options.requestTimeout > 0) { - timeoutSnippet = `var promise = Promise.race([\n${indent}`; - timeoutSnippet += `fetch('${request.url.toString()}', requestOptions)\n${indent}${indent}`; - timeoutSnippet += `.then(response => response.text()),\n${indent}`; - timeoutSnippet += `new Promise((resolve, reject) =>\n${indent}${indent}`; - timeoutSnippet += `setTimeout(() => reject(new Error('Timeout')), ${options.requestTimeout})\n${indent}`; - timeoutSnippet += ')\n]);\n\n'; - timeoutSnippet += 'promise.then(result => console.log(result)),\n'; - timeoutSnippet += 'promise.catch(error => console.log(error));'; - codeSnippet += timeoutSnippet; + if (options.asyncAwaitEnabled) { + fetchSnippet += `try {\n${indent}`; + fetchSnippet += `const response = await fetch("${sanitize(request.url.toString())}", requestOptions);\n${indent}`; + fetchSnippet += `const result = await response.text();\n${indent}`; + fetchSnippet += 'console.log(result)\n'; + fetchSnippet += `} catch (error) {\n${indent}`; + fetchSnippet += 'console.error(error);\n'; + if (options.requestTimeout > 0) { + fetchSnippet += `} finally {\n${indent}`; + fetchSnippet += 'clearTimeout(timerId);\n'; + } + fetchSnippet += '};'; } else { - codeSnippet += fetchSnippet; + fetchSnippet = `fetch("${sanitize(request.url.toString())}", requestOptions)\n${indent}`; + fetchSnippet += `.then((response) => response.text())\n${indent}`; + fetchSnippet += `.then((result) => console.log(result))\n${indent}`; + fetchSnippet += '.catch((error) => console.error(error))'; + if (options.requestTimeout > 0) { + fetchSnippet += `\n${indent}.finally(() => clearTimeout(timerId))`; + } + fetchSnippet += ';'; } + codeSnippet += fetchSnippet; + callback(null, codeSnippet); } diff --git a/codegens/js-fetch/npm-shrinkwrap.json b/codegens/js-fetch/npm-shrinkwrap.json index 42b5ce82c..cf9011a0e 100644 --- a/codegens/js-fetch/npm-shrinkwrap.json +++ b/codegens/js-fetch/npm-shrinkwrap.json @@ -4,58 +4,90 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "formdata-node": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-6.0.3.tgz", + "integrity": "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==", "dev": true }, - "form-data": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", - "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "dev": true, "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "fetch-blob": "^3.1.2" } }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "dev": true }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "dev": true, "requires": { - "mime-db": "1.44.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" } }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "node-fetch2": { + "version": "npm:node-fetch@2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true + }, + "web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } } } } diff --git a/codegens/js-fetch/package.json b/codegens/js-fetch/package.json index 43122bfe5..4d75fe9fe 100644 --- a/codegens/js-fetch/package.json +++ b/codegens/js-fetch/package.json @@ -28,8 +28,9 @@ "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/js-fetch", "dependencies": {}, "devDependencies": { - "form-data": "2.5.1", - "node-fetch": "2.6.7" + "formdata-node": "6.0.3", + "node-fetch2": "npm:node-fetch@2.7.0", + "node-fetch": "3.3.2" }, "engines": { "node": ">=8" diff --git a/codegens/js-fetch/test/newman/newman.test.js b/codegens/js-fetch/test/newman/newman.test.js index 3cc6da264..77c99ab38 100644 --- a/codegens/js-fetch/test/newman/newman.test.js +++ b/codegens/js-fetch/test/newman/newman.test.js @@ -1,9 +1,9 @@ var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, - convert = require('../../lib/index').convert; + convert = require('../../lib/index').convert, + NODE_VERSION = process.versions.node.split('.')[0]; describe('Convert for different types of request', function () { - var testSnippet = 'var fetch = require(\'node-fetch\'),\nFormData = require(\'form-data\'),\n', - testConfig = { + var testConfig = { compileScript: null, runScript: 'node snippet.js', fileName: 'snippet.js', @@ -11,9 +11,23 @@ describe('Convert for different types of request', function () { }, options = { multiLine: true - }; - testSnippet += 'Headers = require(\'node-fetch\').Headers,\n'; - testSnippet += 'URLSearchParams = require(\'url\').URLSearchParams;\n\n'; + }, + testSnippet; + + if (NODE_VERSION < 21) { + testSnippet = 'var fetch = require(\'node-fetch2\');\n'; + } + else { + testSnippet = 'var fetch = (...args) => import(\'node-fetch\').then(({default: fetch}) => fetch(...args));'; + } + + if (NODE_VERSION < 21) { + // Newer node versions ship with built-in FormData, Headers and URLSearchParams class + testSnippet += '\nvar FormData = require(\'formdata-node\').FormData,\n'; + testSnippet += 'Headers = require(\'node-fetch2\').Headers,\n'; + testSnippet += 'URLSearchParams = require(\'url\').URLSearchParams;\n\n'; + } + testConfig.headerSnippet = testSnippet; runNewmanTest(convert, options, testConfig); }); diff --git a/codegens/js-fetch/test/unit/convert.test.js b/codegens/js-fetch/test/unit/convert.test.js index 23105ab1e..182378eda 100644 --- a/codegens/js-fetch/test/unit/convert.test.js +++ b/codegens/js-fetch/test/unit/convert.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), sanitize = require('../../lib/util').sanitize, getOptions = require('../../index').getOptions, convert = require('../../index').convert, @@ -13,7 +13,7 @@ describe('js-fetch convert function for test collection', function () { line_no; it('should return a Space indented snippet ', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { indentType: 'Space', indentCount: 2 @@ -23,11 +23,10 @@ describe('js-fetch convert function for test collection', function () { expect.fail(null, null, error); return; } - expect(snippet).to.be.a('string'); snippetArray = snippet.split('\n'); for (var i = 0; i < snippetArray.length; i++) { - if (snippetArray[i] === 'var requestOptions = {') { line_no = i + 1; } + if (snippetArray[i] === 'const requestOptions = {') { line_no = i + 1; } } expect(snippetArray[line_no].charAt(0)).to.equal(' '); expect(snippetArray[line_no].charAt(1)).to.equal(' '); @@ -35,7 +34,7 @@ describe('js-fetch convert function for test collection', function () { }); it('should return snippet with no setTimeout function when timeout is set to zero', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 0 }; @@ -50,7 +49,7 @@ describe('js-fetch convert function for test collection', function () { }); it('should use JSON.parse if the content-type is application/vnd.api+json', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [ { @@ -85,7 +84,7 @@ describe('js-fetch convert function for test collection', function () { it('should return snippet with redirect property set to manual for ' + 'no follow redirect', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { followRedirect: false }; @@ -95,13 +94,13 @@ describe('js-fetch convert function for test collection', function () { return; } expect(snippet).to.be.a('string'); - expect(snippet).to.include('redirect: \'manual\''); + expect(snippet).to.include('redirect: "manual"'); }); }); it('should return snippet with redirect property set to follow for ' + ' follow redirect', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { followRedirect: true }; @@ -111,12 +110,12 @@ describe('js-fetch convert function for test collection', function () { return; } expect(snippet).to.be.a('string'); - expect(snippet).to.include('redirect: \'follow\''); + expect(snippet).to.include('redirect: "follow"'); }); }); it('should default to mode raw body mode is some random value', function () { - request = new sdk.Request(mainCollection.item[2].request); + request = new Request(mainCollection.item[2].request); request.body.mode = 'random'; request.body[request.body.mode] = {}; options = {}; @@ -131,14 +130,14 @@ describe('js-fetch convert function for test collection', function () { }); it('should generate snippet for no body provided', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'GET', 'url': { - 'raw': 'https://mockbin.org/request', + 'raw': 'https://postman-echo.com/request', 'protocol': 'https', 'host': [ - 'mockbin', - 'org' + 'postman-echo', + 'com' ], 'path': [ 'request' @@ -154,7 +153,7 @@ describe('js-fetch convert function for test collection', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -182,7 +181,7 @@ describe('js-fetch convert function for test collection', function () { }); it('should include JSON.stringify in the snippet for raw json bodies', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -216,7 +215,7 @@ describe('js-fetch convert function for test collection', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -265,7 +264,7 @@ describe('js-fetch convert function for test collection', function () { }); it('should generate valid snippet for single/double quotes in url', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -298,6 +297,62 @@ describe('js-fetch convert function for test collection', function () { expect(snippet).to.include('fetch("https://postman-echo.com/get?query1=b\'b&query2=c\\"c"'); }); }); + + it('should return snippet with promise based code when async_await is disabled', function () { + const request = new Request(mainCollection.item[0].request); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('fetch('); + expect(snippet).to.include('.then((response) => '); + expect(snippet).to.include('.catch((error) => '); + }); + }); + + it('should return snippet with async/await based code when option is enabled', function () { + const request = new Request(mainCollection.item[0].request); + + convert(request, { asyncAwaitEnabled: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('const response = await fetch('); + expect(snippet).to.include('const result = await response.text()'); + expect(snippet).to.include('catch (error) {'); + }); + }); + + it('should return timeout snippet with promise based code when async_await is disabled', function () { + const request = new Request(mainCollection.item[0].request); + + convert(request, { requestTimeout: 3000 }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('const controller'); + expect(snippet).to.include('const timerId'); + expect(snippet).to.include('.finally(() => clearTimeout(timerId))'); + }); + }); + + it('should return timeout snippet with promise based code when async_await is enabled', function () { + const request = new Request(mainCollection.item[0].request); + + convert(request, { requestTimeout: 3000, asyncAwaitEnabled: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('const controller'); + expect(snippet).to.include('const timerId'); + expect(snippet).to.include('} finally {'); + }); + }); }); describe('getOptions function', function () { @@ -312,6 +367,7 @@ describe('js-fetch convert function for test collection', function () { expect(getOptions()[2]).to.have.property('id', 'requestTimeout'); expect(getOptions()[3]).to.have.property('id', 'followRedirect'); expect(getOptions()[4]).to.have.property('id', 'trimRequestBody'); + expect(getOptions()[5]).to.have.property('id', 'asyncAwaitEnabled'); }); }); diff --git a/codegens/js-fetch/test/unit/fixtures/testcollection/collection.json b/codegens/js-fetch/test/unit/fixtures/testcollection/collection.json index f70342d69..32c12564b 100644 --- a/codegens/js-fetch/test/unit/fixtures/testcollection/collection.json +++ b/codegens/js-fetch/test/unit/fixtures/testcollection/collection.json @@ -49,11 +49,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -123,11 +123,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request?test=123&anotherone=232", + "raw": "https://postman-echo.com/request?test=123&anotherone=232", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -197,11 +197,11 @@ "raw": "\"'Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, \"id\" \"se\\\"mper\" sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.'\"" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -369,11 +369,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -431,11 +431,11 @@ "raw": "{\n \"json\": \"Test-Test\"\n}" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -616,11 +616,11 @@ "raw": "var val = 6;\nconsole.log(val);" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -671,11 +671,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -726,11 +726,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -755,14 +755,13 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request/:action", + "raw": "https://postman-echo.com/:action", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ - "request", ":action" ], "variable": [ @@ -815,11 +814,11 @@ "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -868,11 +867,11 @@ "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -939,11 +938,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -991,11 +990,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1018,11 +1017,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1047,11 +1046,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1076,11 +1075,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1105,11 +1104,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1126,11 +1125,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1155,11 +1154,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1184,11 +1183,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1205,13 +1204,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1225,13 +1222,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1320,7 +1315,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1331,11 +1326,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1352,11 +1347,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1468,14 +1463,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/js-jquery/.gitignore b/codegens/js-jquery/.gitignore index 36948272a..ee4bf0664 100644 --- a/codegens/js-jquery/.gitignore +++ b/codegens/js-jquery/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/js-jquery/test/unit/converter.test.js b/codegens/js-jquery/test/unit/converter.test.js index abb78accf..eff18af59 100644 --- a/codegens/js-jquery/test/unit/converter.test.js +++ b/codegens/js-jquery/test/unit/converter.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), fs = require('fs'), convert = require('../../lib/index').convert, @@ -25,7 +25,7 @@ describe('jQuery converter', function () { mainCollection.item.forEach(function (item) { it(item.name, function (done) { - var request = new sdk.Request(item.request); + var request = new Request(item.request); convert(request, {indentType: 'Space', indentCount: 4, requestTimeout: 100, @@ -42,7 +42,7 @@ describe('jQuery converter', function () { }); it('should return snippet without errors when request object has no body property', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -74,7 +74,7 @@ describe('jQuery converter', function () { }); it('should use JSON.parse if the content-type is application/vnd.api+json', function () { - let request = new sdk.Request({ + let request = new Request({ 'method': 'POST', 'header': [ { @@ -108,7 +108,7 @@ describe('jQuery converter', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -134,7 +134,7 @@ describe('jQuery converter', function () { }); }); it('should include JSON.stringify in the snippet for raw json bodies', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -168,7 +168,7 @@ describe('jQuery converter', function () { }); it('should include graphql body in the snippet', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -201,7 +201,7 @@ describe('jQuery converter', function () { }); it('should generate snippets(not error out) for requests with multiple/no file in formdata', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -267,7 +267,7 @@ describe('jQuery converter', function () { }); it('should generate valid snippet for multiple headers with same name', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -304,7 +304,7 @@ describe('jQuery converter', function () { }); it('should generate snippet for form data params with no type key present', function () { - var request = new sdk.Request({ + var request = new Request({ method: 'POST', header: [], url: { diff --git a/codegens/js-jquery/test/unit/fixtures/sample_collection.json b/codegens/js-jquery/test/unit/fixtures/sample_collection.json index bb25478b6..d7f23f1f2 100644 --- a/codegens/js-jquery/test/unit/fixtures/sample_collection.json +++ b/codegens/js-jquery/test/unit/fixtures/sample_collection.json @@ -1147,11 +1147,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1175,11 +1175,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1203,11 +1203,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1226,11 +1226,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1240,7 +1240,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1254,11 +1254,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1282,11 +1282,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1305,13 +1305,11 @@ "raw": "" }, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1327,13 +1325,11 @@ "raw": "" }, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1349,13 +1345,11 @@ "raw": "" }, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, diff --git a/codegens/js-jquery/test/unit/fixtures/snippetFixtures.json b/codegens/js-jquery/test/unit/fixtures/snippetFixtures.json index 70cc665fb..65beeafe3 100644 --- a/codegens/js-jquery/test/unit/fixtures/snippetFixtures.json +++ b/codegens/js-jquery/test/unit/fixtures/snippetFixtures.json @@ -16,12 +16,12 @@ "PATCH Request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/patch%22%2C%0A%20%20%20%20%22method%22%3A%20%22PATCH%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Curabitur%20auctor%2C%20elit%20nec%20pulvinar%20porttitor%2C%20ex%20augue%20condimentum%20enim%2C%20eget%20suscipit%20urna%20felis%20quis%20neque.%5CnSuspendisse%20sit%20amet%20luctus%20massa%2C%20nec%20venenatis%20mi.%20Suspendisse%20tincidunt%20massa%20at%20nibh%20efficitur%20fringilla.%20Nam%20quis%20congue%20mi.%20Etiam%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", "DELETE Request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/delete%22%2C%0A%20%20%20%20%22method%22%3A%20%22DELETE%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Donec%20fermentum%2C%20nisi%20sed%20cursus%20eleifend%2C%20nulla%20tortor%20ultricies%20tellus%2C%20ut%20vehicula%20orci%20arcu%20ut%20velit.%20In%20volutpat%20egestas%20dapibus.%5CnMorbi%20condimentum%20vestibulum%20sapien.%20Etiam%20dignissim%20diam%20quis%20eros%20lobortis%20gravida%20vel%20lobortis%20est.%20Etiam%20gravida%20sed.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", "OPTIONS to postman echo": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/post%22%2C%0A%20%20%20%20%22method%22%3A%20%22OPTIONS%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", - "LINK request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//mockbin.org/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22LINK%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", - "UNLINK request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//mockbin.org/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22UNLINK%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", - "LOCK request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//mockbin.org/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22LOCK%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", - "UNLOCK request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//mockbin.org/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22UNLOCK%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", - "PROFIND request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//mockbin.org/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22PROPFIND%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", - "VIEW request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//mockbin.org/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22VIEW%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", - "PURGE Request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io%22%2C%0A%20%20%20%20%22method%22%3A%20%22PURGE%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", - "COPY Request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io%22%2C%0A%20%20%20%20%22method%22%3A%20%22COPY%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B" + "LINK request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22LINK%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", + "UNLINK request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22UNLINK%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", + "LOCK request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22LOCK%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", + "UNLOCK request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22UNLOCK%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", + "PROPFIND request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22PROPFIND%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", + "VIEW request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com/request%22%2C%0A%20%20%20%20%22method%22%3A%20%22VIEW%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%20%20%20%20%22headers%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22Content-Type%22%3A%20%22text/plain%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22data%22%3A%20%22Duis%20posuere%20augue%20vel%20cursus%20pharetra.%20In%20luctus%20a%20ex%20nec%20pretium.%20Praesent%20neque%20quam%2C%20tincidunt%20nec%20leo%20eget%2C%20rutrum%20vehicula%20magna.%5CnMaecenas%20consequat%20elementum%20elit%2C%20id%20semper%20sem%20tristique%20et.%20Integer%20pulvinar%20enim%20quis%20consectetur%20interdum%20volutpat.%22%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", + "PURGE Request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com%22%2C%0A%20%20%20%20%22method%22%3A%20%22PURGE%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B", + "COPY Request": "var%20settings%20%3D%20%7B%0A%20%20%20%20%22url%22%3A%20%22https%3A//postman-echo.com%22%2C%0A%20%20%20%20%22method%22%3A%20%22COPY%22%2C%0A%20%20%20%20%22timeout%22%3A%20100%2C%0A%7D%3B%0A%0A%24.ajax%28settings%29.done%28function%20%28response%29%20%7B%0A%20%20%20%20console.log%28response%29%3B%0A%7D%29%3B" } diff --git a/codegens/js-xhr/.gitignore b/codegens/js-xhr/.gitignore index 0c6c137f2..10cfd51a9 100644 --- a/codegens/js-xhr/.gitignore +++ b/codegens/js-xhr/.gitignore @@ -12,6 +12,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/js-xhr/lib/index.js b/codegens/js-xhr/lib/index.js index 0390ff596..1b9be55f3 100644 --- a/codegens/js-xhr/lib/index.js +++ b/codegens/js-xhr/lib/index.js @@ -2,6 +2,7 @@ var _ = require('./lodash'), sanitize = require('./util').sanitize, sanitizeOptions = require('./util').sanitizeOptions, addFormParam = require('./util').addFormParam, + getUrlStringfromUrlObject = require('./util').getUrlStringfromUrlObject, path = require('path'); /** @@ -150,6 +151,9 @@ function parseHeaders (headers) { if (!_.isEmpty(headers)) { headers = _.reject(headers, 'disabled'); _.forEach(headers, function (header) { + if (_.capitalize(header.key) === 'Cookie') { + headerSnippet += '// WARNING: Cookies will be stripped away by the browser before sending the request.\n'; + } headerSnippet += `xhr.setRequestHeader("${sanitize(header.key, true)}", "${sanitize(header.value)}");\n`; }); } @@ -263,6 +267,9 @@ function convert (request, options, callback) { bodySnippet = request.body && !_.isEmpty(request.body.toJSON()) ? parseBody(request.body.toJSON(), trim, indent, request.headers.get('Content-Type')) : ''; + if (_.includes(['Get', 'Post'], _.capitalize(request.method))) { + codeSnippet += `// WARNING: For ${request.method} requests, body is set to null by browsers.\n`; + } codeSnippet += bodySnippet + '\n'; codeSnippet += 'var xhr = new XMLHttpRequest();\nxhr.withCredentials = true;\n\n'; @@ -272,7 +279,7 @@ function convert (request, options, callback) { codeSnippet += `${indent.repeat(2)}console.log(this.responseText);\n`; codeSnippet += `${indent}}\n});\n\n`; - codeSnippet += `xhr.open("${request.method}", "${encodeURI(request.url.toString())}");\n`; + codeSnippet += `xhr.open("${request.method}", "${getUrlStringfromUrlObject(request.url)}");\n`; if (options.requestTimeout) { codeSnippet += `xhr.timeout = ${options.requestTimeout};\n`; codeSnippet += 'xhr.addEventListener("ontimeout", function(e) {\n'; diff --git a/codegens/js-xhr/lib/util.js b/codegens/js-xhr/lib/util.js index 71b49c7c6..94fb47ac7 100644 --- a/codegens/js-xhr/lib/util.js +++ b/codegens/js-xhr/lib/util.js @@ -1,4 +1,6 @@ -module.exports = { +const _ = require('./lodash'); + +const self = module.exports = { /** * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' * and trim input if required @@ -87,6 +89,91 @@ module.exports = { return result; }, + /** + * + * @param {Object} urlObject The request sdk request.url object + * @returns {String} The final string after parsing all the parameters of the url including + * protocol, auth, host, port, path, query, hash + * This will be used because the url.toString() method returned the URL with non encoded query string + * and hence a manual call is made to getQueryString() method with encode option set as true. + */ + getUrlStringfromUrlObject: function (urlObject) { + var url = ''; + if (!urlObject) { + return url; + } + if (urlObject.protocol) { + url += (urlObject.protocol.endsWith('://') ? urlObject.protocol : urlObject.protocol + '://'); + } + if (urlObject.auth && urlObject.auth.user) { + url = url + ((urlObject.auth.password) ? + urlObject.auth.user + ':' + urlObject.auth.password : urlObject.auth.user) + '@'; + } + if (urlObject.host) { + url += urlObject.getHost(); + } + if (urlObject.port) { + url += ':' + urlObject.port.toString(); + } + if (urlObject.path) { + url += urlObject.getPath(); + } + if (urlObject.query && urlObject.query.count()) { + let queryString = self.getQueryString(urlObject); + queryString && (url += '?' + queryString); + } + if (urlObject.hash) { + url += '#' + urlObject.hash; + } + + return self.sanitize(url, false); + }, + + /** + * @param {Object} urlObject + * @returns {String} + */ + getQueryString: function (urlObject) { + let isFirstParam = true, + params = _.get(urlObject, 'query.members'), + result = ''; + if (Array.isArray(params)) { + result = _.reduce(params, function (result, param) { + if (param.disabled === true) { + return result; + } + + if (isFirstParam) { + isFirstParam = false; + } + else { + result += '&'; + } + + return result + self.encodeParam(param.key) + '=' + self.encodeParam(param.value); + }, result); + } + + return result; + }, + + /** + * Encode param except the following characters- [,{,},],%,+ + * + * @param {String} param + * @returns {String} + */ + encodeParam: function (param) { + return encodeURIComponent(param) + .replace(/%5B/g, '[') + .replace(/%7B/g, '{') + .replace(/%5D/g, ']') + .replace(/%7D/g, '}') + .replace(/%2B/g, '+') + .replace(/%25/g, '%') + .replace(/'/g, '%27'); + }, + /** * * @param {Array} array - form data array diff --git a/codegens/js-xhr/npm-shrinkwrap.json b/codegens/js-xhr/npm-shrinkwrap.json index 79d5e1865..5c10e5679 100644 --- a/codegens/js-xhr/npm-shrinkwrap.json +++ b/codegens/js-xhr/npm-shrinkwrap.json @@ -4,6 +4,53 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, "xmlhttprequest": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", diff --git a/codegens/js-xhr/test/unit/convert.test.js b/codegens/js-xhr/test/unit/convert.test.js index c36f2226d..4ed5d1853 100644 --- a/codegens/js-xhr/test/unit/convert.test.js +++ b/codegens/js-xhr/test/unit/convert.test.js @@ -1,14 +1,15 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), sanitize = require('../../lib/util.js').sanitize, - + getUrlStringfromUrlObject = require('../../lib/util').getUrlStringfromUrlObject, convert = require('../../index').convert, getOptions = require('../../index').getOptions; describe('js-xhr convert function', function () { it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -36,7 +37,7 @@ describe('js-xhr convert function', function () { }); it('should include JSON.stringify in the snippet for raw json bodies', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -70,7 +71,7 @@ describe('js-xhr convert function', function () { }); it('should use JSON.parse if the content-type is application/vnd.api+json', function () { - let request = new sdk.Request({ + let request = new Request({ 'method': 'POST', 'header': [ { @@ -104,7 +105,7 @@ describe('js-xhr convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -162,6 +163,15 @@ describe('js-xhr convert function', function () { it('should trim input string when needed', function () { expect(sanitize('inputString ', true)).to.equal('inputString'); }); + it('should not encode unresolved query params and ' + + 'encode every other query param, both present together', function () { + let rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\'', + urlObject = new Url(rawUrl), + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b+c%27'); + }); }); describe('POST Form data Request', function () { @@ -223,7 +233,7 @@ describe('js-xhr convert function', function () { 'description': 'The HTTP `POST` request with formData' }, - request = new sdk.Request(req), + request = new Request(req), options = { indentCount: 2, indentType: 'Space', @@ -268,7 +278,7 @@ describe('js-xhr convert function', function () { 'description': 'Request without a body' }, - request = new sdk.Request(req), + request = new Request(req), options = { indentCount: 2, indentType: 'Space' @@ -308,7 +318,7 @@ describe('js-xhr convert function', function () { 'description': 'Request without a body' }, - request = new sdk.Request(req), + request = new Request(req), options = { indentCount: 2, indentType: 'Space' diff --git a/codegens/js-xhr/test/unit/fixtures/testcollection/collection.json b/codegens/js-xhr/test/unit/fixtures/testcollection/collection.json index 8afd06147..04db3c624 100644 --- a/codegens/js-xhr/test/unit/fixtures/testcollection/collection.json +++ b/codegens/js-xhr/test/unit/fixtures/testcollection/collection.json @@ -961,11 +961,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -990,11 +990,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1019,11 +1019,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1040,11 +1040,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1055,7 +1055,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1069,11 +1069,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1098,11 +1098,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1119,13 +1119,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1139,13 +1137,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1234,7 +1230,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1245,11 +1241,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1266,11 +1262,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1382,14 +1378,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/kotlin-okhttp/.gitignore b/codegens/kotlin-okhttp/.gitignore new file mode 100644 index 000000000..9517952b4 --- /dev/null +++ b/codegens/kotlin-okhttp/.gitignore @@ -0,0 +1,76 @@ +*.java +*.class +*.jar + +.DS_Store +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ diff --git a/codegens/kotlin-okhttp/.jsdoc-config.json b/codegens/kotlin-okhttp/.jsdoc-config.json new file mode 100644 index 000000000..90e6d5d44 --- /dev/null +++ b/codegens/kotlin-okhttp/.jsdoc-config.json @@ -0,0 +1,34 @@ +{ + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc", "closure"] + }, + "source": { + "include": [ ], + "includePattern": ".+\\.js(doc)?$", + "excludePattern": "(^|\\/|\\\\)_" + }, + + "plugins": [ + "plugins/markdown" + ], + + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "highlightTutorialCode" : true + }, + + "opts": { + "template": "./node_modules/postman-jsdoc-theme", + "encoding": "utf8", + "destination": "./out/docs", + "recurse": true, + "readme": "README.md" + }, + + "markdown": { + "parser": "gfm", + "hardwrap": false + } +} diff --git a/codegens/kotlin-okhttp/.npmignore b/codegens/kotlin-okhttp/.npmignore new file mode 100644 index 000000000..17156c3bc --- /dev/null +++ b/codegens/kotlin-okhttp/.npmignore @@ -0,0 +1,74 @@ +### NPM Specific: Disregard recursive project files +### =============================================== +/.editorconfig +/.gitmodules +/test + +### Borrowed from .gitignore +### ======================== + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ diff --git a/codegens/kotlin-okhttp/README.md b/codegens/kotlin-okhttp/README.md new file mode 100644 index 000000000..10947318a --- /dev/null +++ b/codegens/kotlin-okhttp/README.md @@ -0,0 +1,74 @@ +# codegen-kotlin-okhttp + +> Converts Postman-SDK Request into code snippet for kotlin-okhttp. + +#### Prerequisites +To run Code-Gen, ensure that you have NodeJS >= v12. A copy of the NodeJS installable can be downloaded from https://nodejs.org/en/download/package-manager. + +## Using the Module +The module will expose an object which will have property `convert` which is the function for converting the Postman-SDK request to kotlin-okhttp code snippet and `getOptions` function which returns an array of supported options. + +### convert function +Convert function will take three parameters +* `request`- Postman-SDK Request object + +* `options`- options is an object which can have following properties + * `indentType`- string representing type of indentation for code snippet. eg: 'Space', 'Tab' + * `indentCount`- positiveInteger representing count of indentation required. + * `includeBoilerplate`- boolean representing whether to include class definition in code snippet + * `requestTimeout` : Integer denoting time after which the request will bail out in milli-seconds + * `trimRequestBody` : Trim request body fields + * `followRedirect` : Boolean denoting whether to redirect a request + +* `callback`- callback function with first parameter as error and second parameter as string for code snippet + +##### Example: +```js +var request = new sdk.Request('www.google.com'), //using postman sdk to create request + options = { + indentType: 'Space', + indentCount: 2, + includeBoilerplate: false + }; +convert(request, options, function(error, snippet) { + if (error) { + // handle error + } + // handle snippet +}); +``` + +### getOptions function + +This function returns a list of options supported by this codegen. + +#### Example +```js +var options = getOptions(); + +console.log(options); +// output +// [ +// { +// name: 'Set indentation count', +// id: 'indentCount', +// type: 'positiveInteger', +// default: 2, +// description: 'Set the number of indentation characters to add per code level' +// }, +// ... +// ] +``` + +### Guideline for using generated snippet +* Generated snippet requires dependencies [okhttp3](https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp/3.9.1) and [okio](https://mvnrepository.com/artifact/com.squareup.okio/okio/1.13.0) to compile and run + +* Generated snippet uses `.method(nameOfMethod, body)` from `Request` class to form HTTP request. If the `method` doesn't require body then the value of `body` will be `null`. + +* Generated snippet uses `MultipartBody.Builder()` when `multipart/formdata` is used otherwise it uses `RequestBody.create()` in order to add body to request. + +* Since Postman-SDK Request object doesn't provide complete path of the file, it needs to be manually inserted in case of uploading a file. + +* `content-type` needs to be specified in order to add body to the request. So if no `content-type` is specified then `text/plain` will be used as default. **In case of `multipart/formdata` `content-type` is generated by snippet itself**. + +* This module doesn't support cookies. diff --git a/codegens/kotlin-okhttp/index.js b/codegens/kotlin-okhttp/index.js new file mode 100644 index 000000000..bb0a047c4 --- /dev/null +++ b/codegens/kotlin-okhttp/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/codegens/kotlin-okhttp/lib/index.js b/codegens/kotlin-okhttp/lib/index.js new file mode 100644 index 000000000..ba63214e1 --- /dev/null +++ b/codegens/kotlin-okhttp/lib/index.js @@ -0,0 +1,4 @@ +module.exports = { + convert: require('./okhttp').convert, + getOptions: require('./okhttp').getOptions +}; diff --git a/codegens/kotlin-okhttp/lib/okhttp.js b/codegens/kotlin-okhttp/lib/okhttp.js new file mode 100644 index 000000000..c8ccd721f --- /dev/null +++ b/codegens/kotlin-okhttp/lib/okhttp.js @@ -0,0 +1,233 @@ +var _ = require('lodash'), + parseRequest = require('./parseRequest'), + sanitize = require('./util').sanitize, + addFormParam = require('./util').addFormParam, + sanitizeOptions = require('./util').sanitizeOptions; + +// Since Kotlin OkHttp requires to add extralines of code to handle methods with body +const METHODS_WITHOUT_BODY = ['GET', 'HEAD', 'COPY', 'UNLOCK', 'UNLINK', 'PURGE', 'LINK', 'VIEW']; + +/** + * returns snippet of kotlin okhttp by parsing data from Postman-SDK request object + * + * @param {Object} request - Postman SDK request object + * @param {String} indentString - indentation required for code snippet + * @param {Object} options - Options to tweak code snippet + * @returns {String} - kotlin okhttp code snippet for given request object + */ +function makeSnippet (request, indentString, options) { + let isBodyRequired = !(_.includes(METHODS_WITHOUT_BODY, request.method)), + snippet = 'val client = OkHttpClient', + hasNoOptions = !(options.requestTimeout || options.followRedirects); + + if (hasNoOptions) { + snippet += '()\n'; + } + else { + snippet += '.Builder()\n'; + if (options.requestTimeout > 0) { + snippet += indentString + `.connectTimeout(${options.requestTimeout}, TimeUnit.SECONDS)\n`; + } + + if (_.get(request, 'protocolProfileBehavior.followRedirects', options.followRedirect) === false) { + snippet += indentString + '.followRedirects(false)\n'; + } + + snippet += indentString + '.build()\n'; + } + + if (isBodyRequired) { + // The following code handles multiple files in the same formdata param. + // It removes the form data params where the src property is an array of filepath strings + // Splits that array into different form data params with src set as a single filepath string + if (request.body && request.body.mode === 'formdata') { + let formdata = request.body.formdata, + formdataArray = []; + formdata.members.forEach((param) => { + let key = param.key, + type = param.type, + disabled = param.disabled, + contentType = param.contentType; + if (type === 'file') { + if (typeof param.src !== 'string') { + if (Array.isArray(param.src) && param.src.length) { + param.src.forEach((filePath) => { + addFormParam(formdataArray, key, param.type, filePath, disabled, contentType); + }); + } + else { + addFormParam(formdataArray, key, param.type, '/path/to/file', disabled, contentType); + } + } + else { + addFormParam(formdataArray, key, param.type, param.src, disabled, contentType); + } + } + else { + addFormParam(formdataArray, key, param.type, param.value, disabled, contentType); + } + }); + request.body.update({ + mode: 'formdata', + formdata: formdataArray + }); + } + + const contentType = parseRequest.parseContentType(request), + requestBody = (request.body ? request.body.toJSON() : {}); + // snippet for creating mediatype object in java based on content-type of request + snippet += `val mediaType = "${contentType}".toMediaType()\n`; + snippet += parseRequest.parseBody(requestBody, indentString, options.trimRequestBody, contentType); + } + + snippet += 'val request = Request.Builder()\n'; + snippet += indentString + `.url("${sanitize(request.url.toString())}")\n`; + if (isBodyRequired) { + switch (request.method) { + case 'POST': + snippet += indentString + '.post(body)\n'; + break; + case 'PUT': + snippet += indentString + '.put(body)\n'; + break; + case 'PATCH': + snippet += indentString + '.patch(body)\n'; + break; + default: + snippet += indentString + `.method("${request.method}", body)\n`; + } + } + if (request.body && request.body.mode === 'graphql' && !request.headers.has('Content-Type')) { + request.addHeader({ + key: 'Content-Type', + value: 'application/json' + }); + } + // kotlin-okhttp snippet for adding headers to request + snippet += parseRequest.parseHeader(request, indentString); + + snippet += indentString + '.build()\n'; + snippet += 'val response = client.newCall(request).execute()'; + + return snippet; +} + +/** + * Used in order to get options for generation of Java okhttp code snippet (i.e. Include Boilerplate code) + * + * @module getOptions + * + * @returns {Array} Options specific to generation of Java okhttp code snippet + */ +function getOptions () { + return [{ + name: 'Include boilerplate', + id: 'includeBoilerplate', + type: 'boolean', + default: false, + description: 'Include class definition and import statements in snippet' + }, + { + name: 'Set indentation count', + id: 'indentCount', + type: 'positiveInteger', + default: 2, + description: 'Set the number of indentation characters to add per code level' + }, + { + name: 'Set indentation type', + id: 'indentType', + type: 'enum', + availableOptions: ['Tab', 'Space'], + default: 'Space', + description: 'Select the character used to indent lines of code' + }, + { + name: 'Set request timeout', + id: 'requestTimeout', + type: 'positiveInteger', + default: 0, + description: 'Set number of milliseconds the request should wait for a response ' + + 'before timing out (use 0 for infinity)' + }, + { + name: 'Follow redirects', + id: 'followRedirect', + type: 'boolean', + default: true, + description: 'Automatically follow HTTP redirects' + }, + { + name: 'Trim request body fields', + id: 'trimRequestBody', + type: 'boolean', + default: false, + description: 'Remove white space and additional lines that may affect the server\'s response' + } + ]; +} + +/** + * Converts Postman sdk request object to java okhttp code snippet + * + * @module convert + * + * @param {Object} request - postman-SDK request object + * @param {Object} options - Options to tweak code snippet generated in kotlin-okhttp + * @param {String} options.indentType - type for indentation eg: Space, Tab + * @param {String} options.indentCount - number of spaces or tabs for indentation. + * @param {Boolean} [options.includeBoilerplate] - indicates whether to include class definition in java + * @param {Boolean} options.followRedirect - whether to enable followredirect + * @param {Boolean} options.trimRequestBody - whether to trim fields in request body or not + * @param {Number} options.requestTimeout : time in milli-seconds after which request will bail out + * @param {Function} callback - callback function with parameters (error, snippet) + */ +function convert (request, options, callback) { + + if (_.isFunction(options)) { + callback = options; + options = {}; + } + else if (!_.isFunction(callback)) { + throw new Error('kotlin-okhttp-Converter: callback is not valid function'); + } + options = sanitizeOptions(options, getOptions()); + // String representing value of indentation required + var indentString, + + // snippets to include java class definition according to options + headerSnippet = '', + footerSnippet = '', + + // snippet to create request in java okhttp + snippet = ''; + + indentString = options.indentType === 'Tab' ? '\t' : ' '; + indentString = indentString.repeat(options.indentCount); + + if (options.includeBoilerplate) { + // TODO: optimize imports + headerSnippet = 'import okhttp3.MediaType.Companion.toMediaType\n' + + 'import okhttp3.MultipartBody\n' + + 'import okhttp3.OkHttpClient\n' + + 'import okhttp3.Request\n' + + 'import okhttp3.RequestBody.Companion.toRequestBody\n' + + 'import okhttp3.RequestBody.Companion.asRequestBody\n' + + 'import java.io.File\n' + + 'import java.util.concurrent.TimeUnit\n\n'; + + footerSnippet = '\n\nprintln(response.body!!.string())\n'; + } + + snippet = makeSnippet(request, indentString, options); + + // if boilerplate is included then two more indentString needs to be added in snippet + // (options.includeBoilerplate) && + // (snippet = indentString.repeat(1) + snippet.split('\n').join('\n' + indentString.repeat(1)) + '\n'); + + return callback(null, headerSnippet + snippet + footerSnippet); +} +module.exports = { + convert: convert, + getOptions: getOptions +}; diff --git a/codegens/kotlin-okhttp/lib/parseRequest.js b/codegens/kotlin-okhttp/lib/parseRequest.js new file mode 100644 index 000000000..72d6357fd --- /dev/null +++ b/codegens/kotlin-okhttp/lib/parseRequest.js @@ -0,0 +1,163 @@ + +var _ = require('lodash'), + sanitize = require('./util').sanitize, + path = require('path'); + +/** + * parses body of request and returns urlencoded string + * + * @param {Object} requestBody - json object respresenting body of request + * @param {Boolean} trimFields - indicates whether to trim fields of body + * @returns {String} - urlencoded string for request body + */ +function parseUrlencode (requestBody, trimFields) { + // reducing array of urlencoded form data to array of strings + return _.reduce(requestBody[requestBody.mode], function (accumalator, data) { + if (!data.disabled) { + accumalator.push(`${sanitize(data.key, trimFields)}=${sanitize(data.value, trimFields)}`.replace(/&/g, '%26')); + } + return accumalator; + }, []).join('&'); +} + +/** + * parses body of request and creates java okhttp code snippet for adding form data + * + * @param {Object} requestBody - JSON object representing body of request + * @param {String} indentString - string for indentation + * @param {Boolean} trimFields - indicates whether to trim fields of body + * @returns {String} - code snippet of java okhttp for multipart formdata + */ +function parseFormData (requestBody, indentString, trimFields) { + return _.reduce(requestBody[requestBody.mode], function (body, data) { + if (data.disabled) { + return body; + } + /* istanbul ignore next */ + if (data.type === 'file') { + var pathArray = data.src.split(path.sep), + fileName = pathArray[pathArray.length - 1]; + body += indentString + '.addFormDataPart' + + `("${sanitize(data.key, trimFields)}","${sanitize(fileName, trimFields)}",\n` + + indentString.repeat(2) + `File("${sanitize(data.src)}")` + + '.asRequestBody("application/octet-stream".toMediaType()))\n'; + } + else { + !data.value && (data.value = ''); + body += `${indentString}.addFormDataPart("${sanitize(data.key, trimFields)}",`; + if (data.contentType) { + body += ` null,\n${indentString.repeat(2)}`; + body += ` "${sanitize(data.value, trimFields)}".toRequestBody("${data.contentType}".toMediaType()))\n`; + } + else { + body += `"${sanitize(data.value, trimFields)}")\n`; + } + } + + return body; + }, '') + indentString + '.build()'; +} + +/** + * Parses request object and returns kotlin okhttp code snippet for raw body + * + * @param {Object} requestBody - JSON object representing body of request + * @param {Boolean} trimFields - indicates whether to trim fields of body + * @param {String} contentType - content type of request body + */ +function parseRawBody (requestBody, trimFields, contentType) { + if (contentType && contentType.startsWith('application/json')) { + return `val body = ${JSON.stringify(requestBody[requestBody.mode])}.toRequestBody(mediaType)\n`; + } + + return `val body = "${sanitize(requestBody[requestBody.mode], trimFields)}".toRequestBody(mediaType)\n`; +} + +/** + * parses request object and returns java okhttp code snippet for adding request body + * + * @param {Object} requestBody - JSON object representing body of request + * @param {String} indentString - string for indentation + * @param {Boolean} trimFields - indicates whether to trim fields of body + * @param {String} contentType - content type of request body + * + * @returns {String} - code snippet of java okhttp parsed from request object + */ +function parseBody (requestBody, indentString, trimFields, contentType) { + if (!_.isEmpty(requestBody)) { + switch (requestBody.mode) { + case 'urlencoded': + return `val body = "${parseUrlencode(requestBody, trimFields)}".toRequestBody(mediaType)\n`; + case 'raw': + return parseRawBody(requestBody, trimFields, contentType); + case 'graphql': + // eslint-disable-next-line no-case-declarations + let query = requestBody[requestBody.mode].query, + graphqlVariables; + try { + graphqlVariables = JSON.parse(requestBody[requestBody.mode].variables); + } + catch (e) { + graphqlVariables = {}; + } + return 'val body = ' + + `"${sanitize(JSON.stringify({ + query: query, + variables: graphqlVariables + }), trimFields)}".toRequestBody(mediaType)\n`; + case 'formdata': + return requestBody.formdata.length ? + 'val body = MultipartBody.Builder().setType(MultipartBody.FORM)\n' + + `${parseFormData(requestBody, indentString, trimFields)}\n` : + 'val body = "{}".toRequestBody("application/json; charset=utf-8".toMediaType())\n'; + /* istanbul ignore next */ + case 'file': + return `val body = File("${requestBody[requestBody.mode].src}")` + + '.asRequestBody("application/octet-stream".toMediaType())\n'; + default: + return 'val body = "".toRequestBody(mediaType)\n'; + } + } + return 'val body = "".toRequestBody(mediaType)\n'; +} + +/** + * Parses header in Postman-SDK request and returns code snippet of java okhttp for adding headers + * + * @param {Object} request - Postman SDK request object + * @param {String} indentString - indentation for code snippet + * @returns {String} - code snippet for adding headers in kotlin-okhttp + */ +function parseHeader (request, indentString) { + var headerArray = request.toJSON().header, + headerSnippet = ''; + + if (!_.isEmpty(headerArray)) { + headerArray = _.reject(headerArray, 'disabled'); + headerSnippet += _.reduce(headerArray, function (accumalator, header) { + accumalator += indentString + `.addHeader("${sanitize(header.key, true)}", ` + + `"${sanitize(header.value)}")\n`; + return accumalator; + }, ''); + } + return headerSnippet; +} + +/** + * returns content-type of request body if available else returns text/plain as default + * + * @param {Object} request - Postman SDK request object + * @returns {String}- content-type of request body + */ +function parseContentType (request) { + if (request.body && request.body.mode === 'graphql') { + return 'application/json'; + } + return request.getHeaders({enabled: true, ignoreCase: true})['content-type'] || 'text/plain'; +} + +module.exports = { + parseBody: parseBody, + parseHeader: parseHeader, + parseContentType: parseContentType +}; diff --git a/codegens/kotlin-okhttp/lib/util.js b/codegens/kotlin-okhttp/lib/util.js new file mode 100644 index 000000000..57248125e --- /dev/null +++ b/codegens/kotlin-okhttp/lib/util.js @@ -0,0 +1,124 @@ +module.exports = { + /** + * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' + * and trim input if required + * + * @param {String} inputString - Input string being sanitized + * @param {Boolean} [trim] - indicates whether to trim string or not + * @returns {String} + */ + sanitize: function (inputString, trim) { + if (typeof inputString !== 'string') { + return ''; + } + inputString = inputString + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\$/g, '\\$') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t'); + return trim ? inputString.trim() : inputString; + + }, + + /** + * sanitizes input options + * + * @param {Object} options - Options provided by the user + * @param {Array} optionsArray - options array received from getOptions function + * + * @returns {Object} - Sanitized options object + */ + sanitizeOptions: function (options, optionsArray) { + var result = {}, + defaultOptions = {}, + id; + optionsArray.forEach((option) => { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + + for (id in options) { + if (options.hasOwnProperty(id)) { + if (defaultOptions[id] === undefined) { + continue; + } + switch (defaultOptions[id].type) { + case 'boolean': + if (typeof options[id] !== 'boolean') { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'positiveInteger': + if (typeof options[id] !== 'number' || options[id] < 0) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'enum': + if (!defaultOptions[id].availableOptions.includes(options[id])) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + default: + result[id] = options[id]; + } + } + } + + for (id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + if (result[id] === undefined) { + result[id] = defaultOptions[id].default; + } + } + } + return result; + }, + + /** + * + * @param {Array} array - form data array + * @param {String} key - key of form data param + * @param {String} type - type of form data param(file/text) + * @param {String} val - value/src property of form data param + * @param {String} disabled - Boolean denoting whether the param is disabled or not + * @param {String} contentType - content type header of the param + * + * Appends a single param to form data array + */ + addFormParam: function (array, key, type, val, disabled, contentType) { + if (type === 'file') { + array.push({ + key: key, + type: type, + src: val, + disabled: disabled, + contentType: contentType + }); + } + else { + array.push({ + key: key, + type: type, + value: val, + disabled: disabled, + contentType: contentType + }); + } + } +}; diff --git a/codegens/kotlin-okhttp/npm-shrinkwrap.json b/codegens/kotlin-okhttp/npm-shrinkwrap.json new file mode 100644 index 000000000..b9bf4c2a7 --- /dev/null +++ b/codegens/kotlin-okhttp/npm-shrinkwrap.json @@ -0,0 +1,13 @@ +{ + "name": "@postman/codegen-kotlin-okhttp", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + } + } +} diff --git a/codegens/kotlin-okhttp/npm/test-lint.js b/codegens/kotlin-okhttp/npm/test-lint.js new file mode 100644 index 000000000..2f4db0cb8 --- /dev/null +++ b/codegens/kotlin-okhttp/npm/test-lint.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +var shell = require('shelljs'), + chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/kotlin-okhttp/npm/test-newman.js b/codegens/kotlin-okhttp/npm/test-newman.js new file mode 100644 index 000000000..ae7d2afe1 --- /dev/null +++ b/codegens/kotlin-okhttp/npm/test-newman.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all newman tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'newman'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running newman tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/kotlin-okhttp/npm/test-unit.js b/codegens/kotlin-okhttp/npm/test-unit.js new file mode 100755 index 000000000..0de7fd529 --- /dev/null +++ b/codegens/kotlin-okhttp/npm/test-unit.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running unit tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/kotlin-okhttp/npm/test.js b/codegens/kotlin-okhttp/npm/test.js new file mode 100755 index 000000000..2c454fb5d --- /dev/null +++ b/codegens/kotlin-okhttp/npm/test.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node +var chalk = require('chalk'), + exit = require('shelljs').exit, + prettyms = require('pretty-ms'), + startedAt = Date.now(), + name = require('../package.json').name; + +require('async').series([ + require('./test-lint'), + require('./test-unit'), + require('./test-newman') +], function (code) { + // eslint-disable-next-line max-len + console.info(chalk[code ? 'red' : 'green'](`\n${name}: duration ${prettyms(Date.now() - startedAt)}\n${name}: ${code ? 'not ok' : 'ok'}!`)); + exit(code && (typeof code === 'number' ? code : 1) || 0); +}); diff --git a/codegens/kotlin-okhttp/package.json b/codegens/kotlin-okhttp/package.json new file mode 100644 index 000000000..40378aa86 --- /dev/null +++ b/codegens/kotlin-okhttp/package.json @@ -0,0 +1,36 @@ +{ + "name": "@postman/codegen-kotlin-okhttp", + "version": "0.0.1", + "description": "Converts postman request into kotlin okhttp code snippet", + "com_postman_plugin": { + "type": "code_generator", + "lang": "Kotlin", + "variant": "Okhttp", + "syntax_mode": "kotlin" + }, + "main": "index.js", + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "node npm/test.js", + "test-lint": "node npm/test-lint.js", + "test-unit": "node npm/test-unit.js", + "test-newman": "node npm/test-newman.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "Postman Labs ", + "license": "Apache-2.0", + "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/kotlin-okhttp", + "dependencies": { + "lodash": "4.17.21" + }, + "devDependencies": {}, + "engines": { + "node": ">=12" + } +} diff --git a/codegens/kotlin-okhttp/test/.eslintrc b/codegens/kotlin-okhttp/test/.eslintrc new file mode 100644 index 000000000..9d477e31e --- /dev/null +++ b/codegens/kotlin-okhttp/test/.eslintrc @@ -0,0 +1,30 @@ +{ + "plugins": [ + "mocha" + ], + "env": { + "mocha": true, + "node": true, + "es6": true + }, + "rules": { + // Mocha + "mocha/handle-done-callback": "error", + "mocha/max-top-level-suites": "error", + "mocha/no-exclusive-tests": "error", + "mocha/no-global-tests": "error", + "mocha/no-hooks-for-single-case": "off", + "mocha/no-hooks": "off", + "mocha/no-identical-title": "error", + "mocha/no-mocha-arrows": "error", + "mocha/no-nested-tests": "error", + "mocha/no-pending-tests": "error", + "mocha/no-return-and-callback": "error", + "mocha/no-sibling-hooks": "error", + "mocha/no-skipped-tests": "warn", + "mocha/no-synchronous-tests": "off", + "mocha/no-top-level-hooks": "warn", + "mocha/valid-test-description": "off", + "mocha/valid-suite-description": "off" + } +} diff --git a/codegens/kotlin-okhttp/test/ci-install.sh b/codegens/kotlin-okhttp/test/ci-install.sh new file mode 100755 index 000000000..1a4cf8853 --- /dev/null +++ b/codegens/kotlin-okhttp/test/ci-install.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/kotlin-okhttp" +pushd ./codegens/kotlin-okhttp &>/dev/null; + sudo add-apt-repository ppa:openjdk-r/ppa -y + sudo rm -rf /var/lib/apt/lists/* + sudo apt-get update + sudo apt-get install -y openjdk-8-jdk + sudo snap install --classic kotlin + unzip test/unit/fixtures/dependencies.zip +popd &>/dev/null; diff --git a/codegens/kotlin-okhttp/test/newman/newman.test.js b/codegens/kotlin-okhttp/test/newman/newman.test.js new file mode 100644 index 000000000..59f97fcb4 --- /dev/null +++ b/codegens/kotlin-okhttp/test/newman/newman.test.js @@ -0,0 +1,13 @@ +var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, + convert = require('../../lib/index').convert; + +describe('convert for different request types', function () { + let options = {indentCount: 3, indentType: 'Space', includeBoilerplate: true}, + testConfig = { + compileScript: null, + runScript: 'kotlinc -script main.kts -cp okhttp-4.10.0.jar:okio-2.10.0.jar', + fileName: 'main.kts', + skipCollections: ['redirectCollection', 'unsupportedMethods'] + }; + runNewmanTest(convert, options, testConfig); +}); diff --git a/codegens/kotlin-okhttp/test/unit/.gitkeep b/codegens/kotlin-okhttp/test/unit/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/codegens/kotlin-okhttp/test/unit/convert.test.js b/codegens/kotlin-okhttp/test/unit/convert.test.js new file mode 100644 index 000000000..94f87130a --- /dev/null +++ b/codegens/kotlin-okhttp/test/unit/convert.test.js @@ -0,0 +1,195 @@ +var expect = require('chai').expect, + { Request } = require('postman-collection/lib/collection/request'), + sanitize = require('../../lib/util').sanitize, + convert = require('../../lib/index').convert, + getOptions = require('../../lib/index').getOptions, + mainCollection = require('../../../../test/codegen/newman/fixtures/basicCollection.json'); + +describe('okhttp convert function', function () { + describe('convert function', function () { + var request = new Request(mainCollection.item[0].request); + + it('should generate snippet with default options given no options', function () { + convert(request, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + return; + } + expect(snippet).to.include('val client = OkHttpClient()\n'); + }); + }); + + it('should return snippet with boilerplate code given option', function () { + convert(request, { includeBoilerplate: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + return; + } + let headerSnippet = 'import okhttp3.MediaType.Companion.toMediaType\n' + + 'import okhttp3.MultipartBody\n' + + 'import okhttp3.OkHttpClient\n' + + 'import okhttp3.Request\n' + + 'import okhttp3.RequestBody.Companion.toRequestBody\n' + + 'import okhttp3.RequestBody.Companion.asRequestBody\n' + + 'import java.io.File\n' + + 'import java.util.concurrent.TimeUnit\n\n'; + expect(snippet).to.include(headerSnippet); + expect(snippet).to.include('println(response.body!!.string())'); + }); + }); + + it('should return snippet with requestTimeout given option', function () { + convert(request, { requestTimeout: 1000 }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + return; + } + expect(snippet).to.include('.connectTimeout(1000, TimeUnit.SECONDS)'); + }); + }); + + it('should return snippet without followRedirect given option', function () { + convert(request, { followRedirect: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + return; + } + expect(snippet).to.not.include('.followRedirects(false)'); + }); + }); + + it('should trim header keys and not trim header values', function () { + var request = new Request({ + 'method': 'GET', + 'header': [ + { + 'key': ' key_containing_whitespaces ', + 'value': ' value_containing_whitespaces ' + } + ], + 'url': { + 'raw': 'https://google.com', + 'protocol': 'https', + 'host': [ + 'google', + 'com' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('.addHeader("key_containing_whitespaces", " value_containing_whitespaces ")'); + }); + }); + + it('should add content type if formdata field contains a content-type', function () { + request = new Request({ + 'method': 'POST', + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'json', + 'value': '{"hello": "world"}', + 'contentType': 'application/json', + 'type': 'text' + } + ] + }, + 'url': { + 'raw': 'http://postman-echo.com/post', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('"{\\"hello\\": \\"world\\"}".toRequestBody("application/json".toMediaType())'); + }); + }); + + it('should generate snippets for no files in form data', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'no file', + 'value': '', + 'type': 'file', + 'src': [] + }, + { + 'key': 'no src', + 'value': '', + 'type': 'file' + }, + { + 'key': 'invalid src', + 'value': '', + 'type': 'file', + 'src': {} + } + ] + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('"no file","file"'); + expect(snippet).to.include('"no src","file"'); + expect(snippet).to.include('"invalid src","file"'); + expect(snippet).to.include('File("/path/to/file")'); + }); + }); + }); + + describe('getOptions function', function () { + it('should return array of options for kotlin-okhttp converter', function () { + expect(getOptions()).to.be.an('array'); + }); + + it('should return all the valid options', function () { + expect(getOptions()[0]).to.have.property('id', 'includeBoilerplate'); + expect(getOptions()[1]).to.have.property('id', 'indentCount'); + expect(getOptions()[2]).to.have.property('id', 'indentType'); + expect(getOptions()[3]).to.have.property('id', 'requestTimeout'); + expect(getOptions()[4]).to.have.property('id', 'followRedirect'); + expect(getOptions()[5]).to.have.property('id', 'trimRequestBody'); + }); + }); + + describe('sanitize function', function () { + it('should handle invalid parameters', function () { + expect(sanitize(123, false)).to.equal(''); + expect(sanitize(' inputString', true)).to.equal('inputString'); + }); + }); +}); diff --git a/codegens/kotlin-okhttp/test/unit/fixtures/dependencies.zip b/codegens/kotlin-okhttp/test/unit/fixtures/dependencies.zip new file mode 100644 index 000000000..9dfcb3179 Binary files /dev/null and b/codegens/kotlin-okhttp/test/unit/fixtures/dependencies.zip differ diff --git a/codegens/kotlin-okhttp/test/unit/fixtures/testcollection/collection.json b/codegens/kotlin-okhttp/test/unit/fixtures/testcollection/collection.json new file mode 100644 index 000000000..5862ab49b --- /dev/null +++ b/codegens/kotlin-okhttp/test/unit/fixtures/testcollection/collection.json @@ -0,0 +1,983 @@ +{ + "info": { + "name": "Code-Gen Test Cases", + "_postman_id": "41182fad-912e-6bc9-d6b9-dfb6f5bf5ffb", + "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Request Headers with disabled headers", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "try {", + " tests[\"Body contains headers\"] = responseBody.has(\"headers\");", + " responseJSON = JSON.parse(responseBody);", + " tests[\"Header contains host\"] = \"host\" in responseJSON.headers;", + " tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;", + "}", + "catch (e) { }", + "", + "", + "", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "not-disabled-header", + "value": "ENABLED" + }, + { + "key": "disabled header", + "value": "DISABLED", + "disabled": true + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "GET Request with disabled query", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "tests['response json contains headers'] = _.has(responseJSON, 'headers');", + "tests['response json contains args'] = _.has(responseJSON, 'args');", + "tests['response json contains url'] = _.has(responseJSON, 'url');", + "", + "tests['args key contains argument passed as url parameter'] = ('test' in responseJSON.args);", + "tests['args passed via request url params has value \"123\"'] = (_.get(responseJSON, 'args.test') === \"123\");" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/get?test=123&anotherone=232", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "test", + "value": "123", + "equals": true + }, + { + "key": "anotherone", + "value": "232", + "equals": true + }, + { + "key": "anotheroneone", + "value": "sdfsdf", + "equals": true, + "disabled": true + } + ] + }, + "description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested." + }, + "response": [] + }, + { + "name": "POST Raw Text", + "event": [ + { + "listen": "test", + "script": { + "id": "753f8a33-adb6-402f-8d19-386c1981ecb6", + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "\"'Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, \"id\" \"se\\\"mper\" sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.'\"" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST form data with file", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded", + "disabled": true + }, + { + "key": "content-type", + "value": "application/json", + "disabled": true + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "fdjks", + "value": "dsf", + "type": "text" + }, + { + "key": "sdf", + "value": "", + "type": "text" + }, + { + "key": "12", + "value": "\"23\"", + "description": "", + "type": "text" + }, + { + "key": "'123'", + "value": "'\"23\\\"4\\\"\"'", + "description": "", + "type": "text" + }, + { + "key": "", + "value": "", + "description": "", + "type": "text", + "disabled": true + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST urlencoded data with disabled entries", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "1", + "value": "a", + "description": "", + "type": "text" + }, + { + "key": "2", + "value": "b", + "description": "", + "type": "text", + "disabled": true + }, + { + "key": "\"\"12\"\"", + "value": "\"23\"", + "description": "", + "type": "text" + }, + { + "key": "'1\"2\\\"\"3'", + "value": "'1\"23\"4'", + "description": "", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post/?hardik=\"me\"", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post", + "" + ], + "query": [ + { + "key": "hardik", + "value": "\"me\"", + "equals": true + } + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST json with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [ + { + "id": "db02f994-5ac4-41e1-835a-f49a14acbb6e", + "name": "POST json with raw", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "Lets a server whitelist headers that browsers are allowed to access." + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Length", + "value": "385", + "name": "Content-Length", + "description": "The length of the response body in octets (8-bit bytes)" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Wed, 07 Feb 2018 10:06:15 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "ETag", + "value": "W/\"215-u7EU1nFtauIn0/aVifjuXA\"", + "name": "ETag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Vary", + "value": "X-HTTP-Method-Override, Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + }, + { + "key": "set-cookie", + "value": "sails.sid=s%3AxRBxgrc9M-jKK_l1mX3y3rM_ry8wYLz4.Of4qpOzd9hi6uO0sAQIj%2Bxs2VeppWxYjJa4OpIW3PKg; Path=/; HttpOnly", + "name": "set-cookie", + "description": "an HTTP cookie" + } + ], + "cookie": [ + { + "expires": "Tue Jan 19 2038 08:44:07 GMT+0530 (IST)", + "httpOnly": true, + "domain": "postman-echo.com", + "path": "/", + "secure": false, + "value": "s%3AxRBxgrc9M-jKK_l1mX3y3rM_ry8wYLz4.Of4qpOzd9hi6uO0sAQIj%2Bxs2VeppWxYjJa4OpIW3PKg", + "key": "sails.sid" + } + ], + "body": "{\"args\":{},\"data\":\"{\\n \\\"json\\\": \\\"Test-Test\\\"\\n}\",\"files\":{},\"form\":{},\"headers\":{\"host\":\"postman-echo.com\",\"content-length\":\"25\",\"accept\":\"*/*\",\"accept-encoding\":\"gzip, deflate\",\"cache-control\":\"no-cache\",\"content-type\":\"text/plain\",\"cookie\":\"sails.sid=s%3AkOgtF1XmXtVFx-Eg3S7-37BKKaMqMDPe.hnwldNwyvsaASUiRR0Y0vcowadkMXO4HMegTeVIPgqo\",\"postman-token\":\"2ced782f-a141-428e-8af6-04ce954a77d5\",\"user-agent\":\"PostmanRuntime/7.1.1\",\"x-forwarded-port\":\"443\",\"x-forwarded-proto\":\"https\"},\"json\":null,\"url\":\"https://postman-echo.com/post\"}" + } + ] + }, + { + "name": "POST javascript with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/javascript" + } + ], + "body": { + "mode": "raw", + "raw": "var val = 6;\nconsole.log(val);" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/xml with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/xml" + } + ], + "body": { + "mode": "raw", + "raw": "\n Test Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/html with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/html" + } + ], + "body": { + "mode": "raw", + "raw": "\n Test Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Resolve URL", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/:action", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + ":action" + ], + "variable": [ + { + "key": "action", + "value": "post" + } + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PUT Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + }, + "description": "The HTTP `PUT` request method is similar to HTTP `POST`. It too is meant to \ntransfer data to a server (and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `PUT` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following \nraw HTTP request,\n\n> PUT /hi/there?hand=wave\n>\n> \n\n\n" + }, + "response": [] + }, + { + "name": "PATCH Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." + }, + "url": { + "raw": "https://postman-echo.com/patch", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "patch" + ] + }, + "description": "The HTTP `PATCH` method is used to update resources on a server. The exact\nuse of `PATCH` requests depends on the server in question. There are a number\nof server implementations which handle `PATCH` differently. Technically, \n`PATCH` supports both Query String parameters and a Request Body.\n\nThis endpoint accepts an HTTP `PATCH` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "DELETE Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + }, + { + "key": "Content-Length", + "value": "1000", + "disabled": true + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "dsfs", + "value": "sfdds", + "description": "", + "type": "text" + }, + { + "key": "sfdsdf", + "value": "sdf", + "description": "", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/delete", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "delete" + ] + }, + "description": "The HTTP `DELETE` method is used to delete resources on a server. The exact\nuse of `DELETE` requests depends on the server implementation. In general, \n`DELETE` requests support both, Query String parameters as well as a Request \nBody.\n\nThis endpoint accepts an HTTP `DELETE` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "OPTIONS to postman echo", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "OPTIONS", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/codegens/kotlin-okhttp/test/unit/validation.test.js b/codegens/kotlin-okhttp/test/unit/validation.test.js new file mode 100644 index 000000000..c1b4ff516 --- /dev/null +++ b/codegens/kotlin-okhttp/test/unit/validation.test.js @@ -0,0 +1,30 @@ +var expect = require('chai').expect, + path = require('path'), + + package = require(path.resolve('.', 'package.json')); + + +describe('package.json', function () { + it('should have com_postman_plugin object with valid properties', function () { + expect(package).to.have.property('com_postman_plugin'); + + expect(package.com_postman_plugin.type).to.equal('code_generator'); + expect(package.com_postman_plugin.lang).to.be.a('string'); + expect(package.com_postman_plugin.variant).to.be.a('string'); + expect(package.com_postman_plugin.syntax_mode).to.be.equal('kotlin'); + }); + it('should have main property with relative path to object with convert property', function () { + var languageModule; + + expect(package.main).to.be.a('string'); + + try { + languageModule = require(path.resolve('.', package.main)); + } + catch (error) { + console.error(error); + } + expect(languageModule).to.be.a('object'); + expect(languageModule.convert).to.be.a('function'); + }); +}); diff --git a/codegens/libcurl/.gitignore b/codegens/libcurl/.gitignore index 610b2e18b..5dd537950 100644 --- a/codegens/libcurl/.gitignore +++ b/codegens/libcurl/.gitignore @@ -14,6 +14,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/libcurl/lib/index.js b/codegens/libcurl/lib/index.js index 255e910ca..d432f98d3 100644 --- a/codegens/libcurl/lib/index.js +++ b/codegens/libcurl/lib/index.js @@ -1,6 +1,7 @@ var sanitize = require('./util').sanitize, sanitizeOptions = require('./util').sanitizeOptions, addFormParam = require('./util').addFormParam, + getUrlStringfromUrlObject = require('./util').getUrlStringfromUrlObject, _ = require('./lodash'), self; @@ -39,7 +40,7 @@ self = module.exports = { snippet += 'if(curl) {\n'; snippet += indentString + `curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "${request.method}");\n`; snippet += indentString + - `curl_easy_setopt(curl, CURLOPT_URL, "${encodeURI(request.url.toString())}");\n`; + `curl_easy_setopt(curl, CURLOPT_URL, "${getUrlStringfromUrlObject(request.url)}");\n`; if (timeout) { snippet += indentString + `curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, ${timeout}L);\n`; } @@ -213,6 +214,9 @@ self = module.exports = { if (body.mode === 'formdata' && options.useMimeType) { snippet += indentString + 'curl_mime_free(mime);\n'; } + if (headersData) { + snippet += indentString + 'curl_slist_free_all(headers);\n'; + } snippet += '}\n'; snippet += 'curl_easy_cleanup(curl);\n'; (options.includeBoilerplate) && diff --git a/codegens/libcurl/lib/util.js b/codegens/libcurl/lib/util.js index 1933ea78e..e81f6187d 100644 --- a/codegens/libcurl/lib/util.js +++ b/codegens/libcurl/lib/util.js @@ -1,4 +1,6 @@ -module.exports = { +const _ = require('./lodash'); + +const self = module.exports = { /** * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' * and trim input if required @@ -89,6 +91,91 @@ module.exports = { return result; }, + /** + * + * @param {Object} urlObject The request sdk request.url object + * @returns {String} The final string after parsing all the parameters of the url including + * protocol, auth, host, port, path, query, hash + * This will be used because the url.toString() method returned the URL with non encoded query string + * and hence a manual call is made to getQueryString() method with encode option set as true. + */ + getUrlStringfromUrlObject: function (urlObject) { + var url = ''; + if (!urlObject) { + return url; + } + if (urlObject.protocol) { + url += (urlObject.protocol.endsWith('://') ? urlObject.protocol : urlObject.protocol + '://'); + } + if (urlObject.auth && urlObject.auth.user) { + url = url + ((urlObject.auth.password) ? + urlObject.auth.user + ':' + urlObject.auth.password : urlObject.auth.user) + '@'; + } + if (urlObject.host) { + url += urlObject.getHost(); + } + if (urlObject.port) { + url += ':' + urlObject.port.toString(); + } + if (urlObject.path) { + url += urlObject.getPath(); + } + if (urlObject.query && urlObject.query.count()) { + let queryString = self.getQueryString(urlObject); + queryString && (url += '?' + queryString); + } + if (urlObject.hash) { + url += '#' + urlObject.hash; + } + + return self.sanitize(url, false); + }, + + /** + * @param {Object} urlObject + * @returns {String} + */ + getQueryString: function (urlObject) { + let isFirstParam = true, + params = _.get(urlObject, 'query.members'), + result = ''; + if (Array.isArray(params)) { + result = _.reduce(params, function (result, param) { + if (param.disabled === true) { + return result; + } + + if (isFirstParam) { + isFirstParam = false; + } + else { + result += '&'; + } + + return result + self.encodeParam(param.key) + '=' + self.encodeParam(param.value); + }, result); + } + + return result; + }, + + /** + * Encode param except the following characters- [,{,},],%,+ + * + * @param {String} param + * @returns {String} + */ + encodeParam: function (param) { + return encodeURIComponent(param) + .replace(/%5B/g, '[') + .replace(/%7B/g, '{') + .replace(/%5D/g, ']') + .replace(/%7D/g, '}') + .replace(/%2B/g, '+') + .replace(/%25/g, '%') + .replace(/'/g, '%27'); + }, + /** * * @param {Array} array - form data array diff --git a/codegens/libcurl/package-lock.json b/codegens/libcurl/package-lock.json deleted file mode 100644 index b650d640f..000000000 --- a/codegens/libcurl/package-lock.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "@postman/codegen-libcurl", - "version": "0.1.0", - "lockfileVersion": 1 -} diff --git a/codegens/libcurl/test/ci-install.sh b/codegens/libcurl/test/ci-install.sh new file mode 100755 index 000000000..de2331a32 --- /dev/null +++ b/codegens/libcurl/test/ci-install.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -ev; # stop on error + +sudo apt-get update + +echo "Installing dependencies required for tests in codegens/libcurl" +sudo apt-get install libcurl4-gnutls-dev diff --git a/codegens/libcurl/test/unit/convert.test.js b/codegens/libcurl/test/unit/convert.test.js index 4f81e14d9..a22176505 100644 --- a/codegens/libcurl/test/unit/convert.test.js +++ b/codegens/libcurl/test/unit/convert.test.js @@ -1,7 +1,9 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), convert = require('../../index').convert, getOptions = require('../../index').getOptions, + getUrlStringfromUrlObject = require('../../lib/util').getUrlStringfromUrlObject, sanitize = require('../../lib/util').sanitize, mainCollection = require('../../../../test/codegen/newman/fixtures/basicCollection.json'); @@ -21,7 +23,7 @@ describe('libcurl convert function', function () { }); it('should set CURLOPT_TIMEOUT_MS parameter when requestTimeout is set to non zero value', function () { - var request = new sdk.Request(mainCollection.item[0].request), + var request = new Request(mainCollection.item[0].request), options = { requestTimeout: 3000 }; convert(request, options, function (error, snippet) { @@ -34,7 +36,7 @@ describe('libcurl convert function', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -62,7 +64,7 @@ describe('libcurl convert function', function () { }); it('should add content type if formdata field contains a content-type', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -97,7 +99,7 @@ describe('libcurl convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -145,6 +147,38 @@ describe('libcurl convert function', function () { expect(snippet).to.include('curl_mime_name(part, "invalid src");'); }); }); + + it('should free up headers list after request is sent', function () { + var request = new Request({ + 'method': 'GET', + 'header': [ + { + 'key': 'Accept', + 'value': 'application/json' + }, + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ], + 'url': { + 'raw': 'https://google.com', + 'protocol': 'https', + 'host': [ + 'google', + 'com' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('curl_slist_free_all(headers)'); + }); + }); + }); describe('getOptions function', function () { @@ -175,5 +209,14 @@ describe('libcurl convert function', function () { expect(sanitize('inputString ', true)).to.equal('inputString'); }); + it('should not encode unresolved query params and ' + + 'encode every other query param, both present together', function () { + let rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\'', + urlObject = new Url(rawUrl), + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b+c%27'); + }); }); }); diff --git a/codegens/libcurl/test/unit/fixtures/testcollection/collection.json b/codegens/libcurl/test/unit/fixtures/testcollection/collection.json index 70b321c5d..7a857e0f1 100644 --- a/codegens/libcurl/test/unit/fixtures/testcollection/collection.json +++ b/codegens/libcurl/test/unit/fixtures/testcollection/collection.json @@ -1047,11 +1047,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1076,11 +1076,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1105,11 +1105,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1126,11 +1126,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1141,7 +1141,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1155,11 +1155,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1184,11 +1184,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1205,13 +1205,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1225,13 +1223,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1320,7 +1316,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1331,11 +1327,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1352,11 +1348,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1468,14 +1464,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/nodejs-axios/.gitignore b/codegens/nodejs-axios/.gitignore index dd41cd99b..f57ef32e8 100644 --- a/codegens/nodejs-axios/.gitignore +++ b/codegens/nodejs-axios/.gitignore @@ -12,6 +12,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # temporarily generated file run.js diff --git a/codegens/nodejs-axios/README.md b/codegens/nodejs-axios/README.md index d4e0b6f20..158bfdd3b 100644 --- a/codegens/nodejs-axios/README.md +++ b/codegens/nodejs-axios/README.md @@ -18,6 +18,7 @@ Convert function will take three parameters * `requestTimeout` : Integer denoting time after which the request will bail out in milli-seconds * `trimRequestBody` : Trim request body fields * `followRedirect` : Boolean denoting whether to redirect a request + * `asyncAwaitEnabled` : Boolean denoting whether to use async/await syntax * `callback`- callback function with first parameter as error and second parameter as string for code snippet @@ -27,7 +28,7 @@ var request = new sdk.Request('www.google.com'), //using postman sdk to create options = { indentType: 'Space', indentCount: 2, - ES6_enabled: true + asyncAwaitEnabled: true }; convert(request, options, function(error, snippet) { if (error) { diff --git a/codegens/nodejs-axios/lib/axios.js b/codegens/nodejs-axios/lib/axios.js index b645fda3f..f720fa04e 100644 --- a/codegens/nodejs-axios/lib/axios.js +++ b/codegens/nodejs-axios/lib/axios.js @@ -1,4 +1,4 @@ -const _ = require('./lodash'), +const _ = require('lodash'), parseRequest = require('./parseRequest'), sanitize = require('./util').sanitize, sanitizeOptions = require('./util').sanitizeOptions, @@ -14,7 +14,7 @@ const _ = require('./lodash'), */ function makeSnippet (request, indentString, options) { - var snippet = options.ES6_enabled ? 'const' : 'var', + let snippet = 'const', configArray = [], dataSnippet = '', body, @@ -84,11 +84,11 @@ function makeSnippet (request, indentString, options) { dataSnippet = !_.isEmpty(body) ? parseRequest.parseBody(body, options.trimRequestBody, indentString, - request.headers.get('Content-Type'), - options.ES6_enabled) : ''; + request.headers.get('Content-Type')) : ''; snippet += dataSnippet + '\n'; configArray.push(indentString + `method: '${request.method.toLowerCase()}'`); + configArray.push(indentString + 'maxBodyLength: Infinity'); configArray.push(indentString + `url: '${sanitize(request.url.toString())}'`); headers = parseRequest.parseHeader(request, indentString); @@ -112,7 +112,7 @@ function makeSnippet (request, indentString, options) { if (options.requestTimeout) { configArray.push(indentString + `timeout: ${options.requestTimeout}`); } - if (options.followRedirect === false) { + if (_.get(request, 'protocolProfileBehavior.followRedirects', options.followRedirect) === false) { // setting the maxRedirects to 0 will disable any redirects. // by default, maxRedirects are set to 5 configArray.push(indentString + 'maxRedirects: 0'); @@ -122,33 +122,31 @@ function makeSnippet (request, indentString, options) { configArray.push(indentString + 'data : data'); } - if (options.ES6_enabled) { - snippet += 'let'; - } - else { - snippet += 'var'; - } - - snippet += ' config = {\n'; + snippet += 'let config = {\n'; snippet += configArray.join(',\n') + '\n'; snippet += '};\n\n'; - snippet += 'axios(config)\n'; - if (options.ES6_enabled) { - snippet += '.then((response) => {\n'; + + if (options.asyncAwaitEnabled) { + snippet += 'async function makeRequest() {\n'; + snippet += indentString + 'try {\n'; + snippet += indentString.repeat(2) + 'const response = await axios.request(config);\n'; + snippet += indentString.repeat(2) + 'console.log(JSON.stringify(response.data));\n'; + snippet += indentString + '}\n'; + snippet += indentString + 'catch (error) {\n'; + snippet += indentString.repeat(2) + 'console.log(error);\n'; + snippet += indentString + '}\n'; + snippet += '}\n\n'; + snippet += 'makeRequest();\n'; } else { - snippet += '.then(function (response) {\n'; - } - snippet += indentString + 'console.log(JSON.stringify(response.data));\n'; - snippet += '})\n'; - if (options.ES6_enabled) { + snippet += 'axios.request(config)\n'; + snippet += '.then((response) => {\n'; + snippet += indentString + 'console.log(JSON.stringify(response.data));\n'; + snippet += '})\n'; snippet += '.catch((error) => {\n'; + snippet += indentString + 'console.log(error);\n'; + snippet += '});\n'; } - else { - snippet += '.catch(function (error) {\n'; - } - snippet += indentString + 'console.log(error);\n'; - snippet += '});\n'; return snippet; } @@ -198,11 +196,11 @@ function getOptions () { description: 'Remove white space and additional lines that may affect the server\'s response' }, { - name: 'Enable ES6 features', - id: 'ES6_enabled', + name: 'Use async/await', + id: 'asyncAwaitEnabled', type: 'boolean', default: false, - description: 'Modifies code snippet to incorporate ES6 (EcmaScript) features' + description: 'Modifies code snippet to use async/await' } ]; } @@ -227,7 +225,7 @@ function convert (request, options, callback) { options = sanitizeOptions(options, getOptions()); // String representing value of indentation required - var indentString; + let indentString; indentString = options.indentType === 'Tab' ? '\t' : ' '; indentString = indentString.repeat(options.indentCount); diff --git a/codegens/nodejs-axios/lib/parseRequest.js b/codegens/nodejs-axios/lib/parseRequest.js index 172e4f110..87c71eefe 100644 --- a/codegens/nodejs-axios/lib/parseRequest.js +++ b/codegens/nodejs-axios/lib/parseRequest.js @@ -1,4 +1,4 @@ -var _ = require('./lodash'), +const _ = require('lodash'), sanitize = require('./util').sanitize; /** @@ -6,12 +6,10 @@ var _ = require('./lodash'), * * @param {Object} body URLEncoded Body * @param {boolean} trim trim body option - * @param {boolean} ES6_enabled ES6 syntax option * @param {string} indentString The indentation string */ -function parseURLEncodedBody (body, trim, ES6_enabled, indentString) { - var varDeclare = ES6_enabled ? 'const' : 'var', - bodySnippet = varDeclare + ' qs = require(\'qs\');\n', +function parseURLEncodedBody (body, trim, indentString) { + let bodySnippet = 'const qs = require(\'qs\');\n', dataArray = []; _.forEach(body, function (data) { @@ -19,13 +17,7 @@ function parseURLEncodedBody (body, trim, ES6_enabled, indentString) { dataArray.push(`'${sanitize(data.key, trim)}': '${sanitize(data.value, trim)}'`); } }); - if (ES6_enabled) { - bodySnippet += 'let'; - } - else { - bodySnippet += 'var'; - } - bodySnippet += ` data = qs.stringify({\n${indentString}${dataArray.join(',\n' + indentString)} \n});`; + bodySnippet += `let data = qs.stringify({\n${indentString}${dataArray.join(',\n' + indentString)} \n});\n`; return bodySnippet; } @@ -34,28 +26,20 @@ function parseURLEncodedBody (body, trim, ES6_enabled, indentString) { * * @param {Object} body FormData body * @param {boolean} trim trim body option - * @param {boolean} ES6_enabled ES6 syntax option */ -function parseFormData (body, trim, ES6_enabled) { - var varDeclare = ES6_enabled ? 'const' : 'var', - bodySnippet = varDeclare + ' FormData = require(\'form-data\');\n'; +function parseFormData (body, trim) { + let bodySnippet = 'const FormData = require(\'form-data\');\n'; // check if there's file const fileArray = body.filter(function (item) { return !item.disabled && item.type === 'file'; }); if (fileArray.length > 0) { - bodySnippet += varDeclare + ' fs = require(\'fs\');\n'; - } - if (ES6_enabled) { - bodySnippet += 'let'; + bodySnippet += 'const fs = require(\'fs\');\n'; } - else { - bodySnippet += 'var'; - } - bodySnippet += ' data = new FormData();\n'; + bodySnippet += 'let data = new FormData();\n'; _.forEach(body, function (data) { if (!data.disabled) { if (data.type === 'file') { - var fileContent = `fs.createReadStream('${data.src}')`; + const fileContent = `fs.createReadStream('${data.src}')`; bodySnippet += `data.append('${sanitize(data.key, trim)}', ${fileContent});\n`; } else { @@ -76,12 +60,10 @@ function parseFormData (body, trim, ES6_enabled) { * @param {Object} body Raw body data * @param {boolean} trim trim body option * @param {String} contentType Content type of the body being sent - * @param {boolean} ES6_enabled ES6 syntax option * @param {String} indentString Indentation string */ -function parseRawBody (body, trim, contentType, ES6_enabled, indentString) { - var varDeclare = ES6_enabled ? 'let' : 'var', - bodySnippet = varDeclare + ' data = '; +function parseRawBody (body, trim, contentType, indentString) { + let bodySnippet = 'let data = '; // Match any application type whose underlying structure is json // For example application/vnd.api+json // All of them have +json as suffix @@ -106,10 +88,8 @@ function parseRawBody (body, trim, contentType, ES6_enabled, indentString) { * @param {Object} body graphql body data * @param {boolean} trim trim body option * @param {String} indentString indentation to be added to the snippet - * @param {boolean} ES6_enabled ES6 syntax option */ -function parseGraphQL (body, trim, indentString, ES6_enabled) { - var varDeclare = ES6_enabled ? 'let' : 'var'; +function parseGraphQL (body, trim, indentString) { let query = body ? body.query : '', graphqlVariables = body ? body.variables : '{}', bodySnippet; @@ -119,22 +99,18 @@ function parseGraphQL (body, trim, indentString, ES6_enabled) { catch (e) { graphqlVariables = {}; } - bodySnippet = varDeclare + ' data = JSON.stringify({\n'; + bodySnippet = 'let data = JSON.stringify({\n'; bodySnippet += `${indentString}query: \`${query ? query.trim() : ''}\`,\n`; bodySnippet += `${indentString}variables: ${JSON.stringify(graphqlVariables)}\n});\n`; return bodySnippet; } -/* istanbul ignore next */ /** - * parses binamry file data - * - * @param {boolean} ES6_enabled ES6 syntax option + * parses binary file data */ -function parseFileData (ES6_enabled) { - var varDeclare = ES6_enabled ? 'let' : 'var', - bodySnippet = varDeclare + ' data = \'\';\n'; +function parseFileData () { + const bodySnippet = 'let data = \'\';\n'; return bodySnippet; } @@ -145,24 +121,23 @@ function parseFileData (ES6_enabled) { * @param {boolean} trim trim body option * @param {String} indentString indentation to be added to the snippet * @param {String} contentType Content type of the body being sent - * @param {boolean} ES6_enabled ES6 syntax option */ -function parseBody (body, trim, indentString, contentType, ES6_enabled) { +function parseBody (body, trim, indentString, contentType) { if (body && !_.isEmpty(body)) { switch (body.mode) { case 'urlencoded': - return parseURLEncodedBody(body.urlencoded, trim, ES6_enabled, indentString); + return parseURLEncodedBody(body.urlencoded, trim, indentString); case 'raw': - return parseRawBody(body.raw, trim, contentType, ES6_enabled, indentString); + return parseRawBody(body.raw, trim, contentType, indentString); case 'graphql': - return parseGraphQL(body.graphql, trim, indentString, ES6_enabled); + return parseGraphQL(body.graphql, trim, indentString); case 'formdata': - return parseFormData(body.formdata, trim, ES6_enabled); + return parseFormData(body.formdata, trim); /* istanbul ignore next */ case 'file': - return parseFileData(ES6_enabled); + return parseFileData(); default: - return parseRawBody(body[body.mode], trim, contentType, ES6_enabled); + return parseRawBody(body[body.mode], trim, contentType); } } return ''; @@ -177,16 +152,13 @@ function parseBody (body, trim, indentString, contentType, ES6_enabled) { * @returns {String} - code snippet of nodejs request to add header */ function parseHeader (request, indentString) { - var headerObject = request.getHeaders({enabled: true}), + let headerObject = request.getHeaders({enabled: true}), headerArray = []; if (!_.isEmpty(headerObject)) { headerArray = _.reduce(Object.keys(headerObject), function (accumalator, key) { if (Array.isArray(headerObject[key])) { - var headerValues = []; - _.forEach(headerObject[key], (value) => { - headerValues.push(`${sanitize(value)}`); - }); + const headerValues = _.map(headerObject[key], (value) => { return `${sanitize(value)}`; }); accumalator.push( indentString.repeat(2) + `'${sanitize(key, true)}': '${headerValues.join(', ')}'` ); diff --git a/codegens/nodejs-axios/lib/util.js b/codegens/nodejs-axios/lib/util.js index 689e6970d..5b5226db5 100644 --- a/codegens/nodejs-axios/lib/util.js +++ b/codegens/nodejs-axios/lib/util.js @@ -11,6 +11,7 @@ function sanitize (inputString, trim) { if (typeof inputString !== 'string') { return ''; } + (trim) && (inputString = inputString.trim()); return inputString.replace(/\\/g, '\\\\').replace(/'/g, '\\\'') .replace(/\n/g, '\\n') @@ -30,49 +31,46 @@ function sanitizeOptions (options, optionsArray) { var result = {}, defaultOptions = {}, id; + optionsArray.forEach((option) => { - defaultOptions[option.id] = { - default: option.default, - type: option.type - }; - if (option.type === 'enum') { - defaultOptions[option.id].availableOptions = option.availableOptions; + const { id, default: defaultValue, type, availableOptions } = option; + defaultOptions[id] = { default: defaultValue, type }; + + if (type === 'enum') { + defaultOptions[id].availableOptions = availableOptions; } }); - for (id in options) { - if (options.hasOwnProperty(id)) { - if (defaultOptions[id] === undefined) { - continue; - } - switch (defaultOptions[id].type) { - case 'boolean': - if (typeof options[id] !== 'boolean') { - result[id] = defaultOptions[id].default; - } - else { - result[id] = options[id]; - } - break; - case 'positiveInteger': - if (typeof options[id] !== 'number' || options[id] < 0) { - result[id] = defaultOptions[id].default; - } - else { - result[id] = options[id]; - } - break; - case 'enum': - if (!defaultOptions[id].availableOptions.includes(options[id])) { - result[id] = defaultOptions[id].default; - } - else { - result[id] = options[id]; - } - break; - default: - result[id] = options[id]; - } + /** + * A type checker object that checks the type of an option value + * + * @typedef {Object} typeCheckers + * + * @property {function(Object, string): boolean} boolean - checks if the option value is a boolean + * @property {function(Object, string): number} positiveInteger - checks if the option value is a positive integer + * @property {function(Object, string): string} enum - checks if the option value is one of the available options + * @property {function(Object, string): *} default - returns the option value without any type checking + * + */ + const typeCheckers = { + boolean: (options, id) => { + return typeof options[id] === 'boolean' ? options[id] : defaultOptions[id].default; + }, + positiveInteger: (options, id) => { + return typeof options[id] === 'number' && options[id] >= 0 ? options[id] : defaultOptions[id].default; + }, + enum: (options, id) => { + return defaultOptions[id].availableOptions.includes(options[id]) ? options[id] : defaultOptions[id].default; + }, + default: (options, id) => { + return options[id]; + } + }; + + for (const id in options) { + if (options.hasOwnProperty(id) && defaultOptions[id] !== undefined) { + const typeChecker = typeCheckers[defaultOptions[id].type] || typeCheckers.default; + result[id] = typeChecker(options, id); } } @@ -98,24 +96,21 @@ function sanitizeOptions (options, optionsArray) { * Appends a single param to form data array */ function addFormParam (array, key, type, val, disabled, contentType) { + const formParam = { + key, + type, + disabled, + contentType + }; + if (type === 'file') { - array.push({ - key: key, - type: type, - src: val, - disabled: disabled, - contentType: contentType - }); + formParam.src = val; } else { - array.push({ - key: key, - type: type, - value: val, - disabled: disabled, - contentType: contentType - }); + formParam.value = val; } + + array.push(formParam); } module.exports = { diff --git a/codegens/nodejs-axios/package-lock.json b/codegens/nodejs-axios/package-lock.json deleted file mode 100644 index 1837d0bfe..000000000 --- a/codegens/nodejs-axios/package-lock.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "name": "@postman/codegen-nodejs-axios", - "version": "0.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "dev": true, - "requires": { - "follow-redirects": "1.5.10" - } - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "dev": true, - "requires": { - "debug": "=3.1.0" - } - }, - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "mime-db": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", - "dev": true - }, - "mime-types": { - "version": "2.1.26", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", - "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", - "dev": true, - "requires": { - "mime-db": "1.43.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "qs": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.2.tgz", - "integrity": "sha512-2eQ6zajpK7HwqrY1rRtGw5IZvjgtELXzJECaEDuzDFo2jjnIXpJSimzd4qflWZq6bLLi+Zgfj5eDrAzl/lptyg==", - "dev": true - } - } -} diff --git a/codegens/nodejs-axios/package.json b/codegens/nodejs-axios/package.json index 3c8d2c674..25de0bed1 100644 --- a/codegens/nodejs-axios/package.json +++ b/codegens/nodejs-axios/package.json @@ -26,13 +26,15 @@ "author": "Postman Labs ", "license": "Apache-2.0", "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/nodejs-axios", - "dependencies": {}, + "dependencies": { + "lodash": "4.17.21" + }, "devDependencies": { - "axios": "0.19.2", - "form-data": "3.0.0", + "axios": "1.7.9", + "form-data": "4.0.2", "qs": "6.9.2" }, "engines": { - "node": ">=8" + "node": ">=12" } } diff --git a/codegens/nodejs-axios/test/newman/newman.test.js b/codegens/nodejs-axios/test/newman/newman.test.js index faa8cac1a..8d97acfed 100644 --- a/codegens/nodejs-axios/test/newman/newman.test.js +++ b/codegens/nodejs-axios/test/newman/newman.test.js @@ -1,24 +1,26 @@ -var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, +const runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, convert = require('../../lib/index').convert; describe('Convert for different types of request', function () { - var options = {indentCount: 2, indentType: 'Space'}, + const options = {indentCount: 2, indentType: 'Space'}, testConfig = { compileScript: null, runScript: 'node run.js', fileName: 'run.js', - headerSnippet: '/* eslint-disable */\n' + headerSnippet: '/* eslint-disable */\n', + skipCollections: ['unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); - describe('Convert for request incorporating ES6 features', function () { - var options = {indentCount: 2, indentType: 'Space', ES6_enabled: true}, + describe('Convert for request incorporating async/await features', function () { + const options = {indentCount: 2, indentType: 'Space', asyncAwaitEnabled: true}, testConfig = { compileScript: null, runScript: 'node run.js', fileName: 'run.js', - headerSnippet: '/* eslint-disable */\n' + headerSnippet: '/* eslint-disable */\n', + skipCollections: ['unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); diff --git a/codegens/nodejs-axios/test/unit/fixtures/testcollection/collection.json b/codegens/nodejs-axios/test/unit/fixtures/testcollection/collection.json index 186aa9ce5..82e46b24f 100644 --- a/codegens/nodejs-axios/test/unit/fixtures/testcollection/collection.json +++ b/codegens/nodejs-axios/test/unit/fixtures/testcollection/collection.json @@ -991,13 +991,11 @@ ], "body": {}, "url": { - "raw": "https://bf1621bb-f962-46b8-bf28-459e03b513ff.mock.pstmn.io/head", + "raw": "https://postman-echo.com/head", "protocol": "https", "host": [ - "bf1621bb-f962-46b8-bf28-459e03b513ff", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "head" @@ -1022,11 +1020,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1051,11 +1049,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1080,11 +1078,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1101,11 +1099,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1130,11 +1128,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1159,11 +1157,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1180,13 +1178,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1200,13 +1196,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1295,7 +1289,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1306,13 +1300,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1325,13 +1317,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1341,7 +1331,7 @@ "header": [], "cookie": [], "responseTime": "0", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] } diff --git a/codegens/nodejs-axios/test/unit/snippet.test.js b/codegens/nodejs-axios/test/unit/snippet.test.js index 314c79050..61e804a4a 100644 --- a/codegens/nodejs-axios/test/unit/snippet.test.js +++ b/codegens/nodejs-axios/test/unit/snippet.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), sanitize = require('../../lib/util').sanitize, parseBody = require('../../lib/parseRequest').parseBody, getOptions = require('../../lib/index').getOptions, @@ -8,14 +8,14 @@ var expect = require('chai').expect, describe('nodejs-axios convert function', function () { describe('Convert function', function () { - var request, + let request, reqObject, options = {}, snippetArray, line_no; it('should return a Tab indented snippet ', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { indentType: 'Tab', indentCount: 1 @@ -23,20 +23,19 @@ describe('nodejs-axios convert function', function () { convert(request, options, function (error, snippet) { if (error) { expect.fail(null, null, error); - return; } expect(snippet).to.be.a('string'); snippetArray = snippet.split('\n'); for (var i = 0; i < snippetArray.length; i++) { - if (snippetArray[i] === 'var config = {') { line_no = i + 1; } + if (snippetArray[i] === 'let config = {') { line_no = i + 1; } } expect(snippetArray[line_no].charAt(0)).to.equal('\t'); }); }); it('should return snippet with timeout property when timeout is set to non zero', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000 }; @@ -51,7 +50,7 @@ describe('nodejs-axios convert function', function () { }); it('should use JSON.parse if the content-type is application/vnd.api+json', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [ { @@ -84,20 +83,59 @@ describe('nodejs-axios convert function', function () { }); }); - it('should return snippet with maxRedirects property set to ' + - '0 for no follow redirect', function () { - request = new sdk.Request(mainCollection.item[0].request); - options = { - followRedirect: false - }; - convert(request, options, function (error, snippet) { - if (error) { - expect.fail(null, null, error); - return; - } + describe('maxRedirects property', function () { + it('should return snippet with maxRedirects property set to ' + + '0 for no follow redirect', function () { + const request = new Request(mainCollection.item[0].request); + options = { + followRedirect: false + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } - expect(snippet).to.be.a('string'); - expect(snippet).to.include('maxRedirects: 0'); + expect(snippet).to.be.a('string'); + expect(snippet).to.include('maxRedirects: 0'); + }); + }); + + it('should return snippet with maxRedirects property set to ' + + '0 for no follow redirect from request settings', function () { + const request = new Request(mainCollection.item[0].request), + options = {}; + + request.protocolProfileBehavior = { + followRedirects: false + }; + + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).to.be.a('string'); + expect(snippet).to.include('maxRedirects: 0'); + }); + }); + + it('should return snippet with no maxRedirects property when ' + + 'follow redirect is true from request settings', function () { + const request = new Request(mainCollection.item[0].request), + options = {}; + + request.protocolProfileBehavior = { + followRedirects: true + }; + + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('maxRedirects'); + }); }); }); @@ -107,7 +145,7 @@ describe('nodejs-axios convert function', function () { 'url': 'https://echo.getpostman.com/post', 'method': 'POST' }; - request = new sdk.Request(reqObject); + request = new Request(reqObject); convert(request, options, function (error, snippet) { if (error) { expect.fail(null, null, error); @@ -119,7 +157,7 @@ describe('nodejs-axios convert function', function () { }); it('should not fail for a random body mode', function () { - request = new sdk.Request(mainCollection.item[2].request); + request = new Request(mainCollection.item[2].request); request.body.mode = 'random'; request.body[request.body.mode] = {}; @@ -135,7 +173,7 @@ describe('nodejs-axios convert function', function () { }); it('should generate snippet for file body mode', function () { - request = new sdk.Request({ + request = new Request({ 'url': 'https://echo.getpostman.com/post', 'method': 'POST', 'body': { @@ -160,7 +198,7 @@ describe('nodejs-axios convert function', function () { }); it('should add content type if formdata field contains a content-type', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -214,7 +252,7 @@ describe('nodejs-axios convert function', function () { console.log(error); }); */ - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = {}; convert(request, options, function (error, snippet) { if (error) { @@ -228,14 +266,14 @@ describe('nodejs-axios convert function', function () { } }); // -2 because last one is a newline - const lastLine = snippetArray[snippetArray.length - 2] + const lastLine = snippetArray[snippetArray.length - 2]; expect(lastLine.charAt(lastLine.length - 1)).to.equal(';'); }); }); it('should return snippet with no trailing comma when requestTimeout ' + 'is set to non zero', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000 }; @@ -253,7 +291,7 @@ describe('nodejs-axios convert function', function () { it('should return snippet with just a single comma when requestTimeout ' + 'is set to non zero and followRedirect as false', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000, followRedirect: false, @@ -273,7 +311,7 @@ describe('nodejs-axios convert function', function () { }); it('should not require unused fs', function () { - request = new sdk.Request({ + request = new Request({ 'url': 'https://postman-echo.com/get', 'method': 'GET', 'body': { @@ -291,7 +329,7 @@ describe('nodejs-axios convert function', function () { }); it('should add fs for form-data file upload', function () { - request = new sdk.Request({ + request = new Request({ 'url': 'https://postman-echo.com/post', 'method': 'POST', 'body': { @@ -310,12 +348,12 @@ describe('nodejs-axios convert function', function () { expect.fail(null, null, error); } expect(snippet).to.be.a('string'); - expect(snippet).to.include('var data = new FormData()'); + expect(snippet).to.include('let data = new FormData()'); }); }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -342,7 +380,7 @@ describe('nodejs-axios convert function', function () { }); it('should include JSON.stringify in the snippet for raw json bodies', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -376,7 +414,7 @@ describe('nodejs-axios convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -424,6 +462,50 @@ describe('nodejs-axios convert function', function () { }); }); + it('should return snippet with maxBodyLength property as "Infinity"', function () { + request = new Request(mainCollection.item[0].request); + options = { + requestTimeout: 1000 + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + return; + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('maxBodyLength: Infinity'); + }); + }); + + it('should return snippet with promise based code when async_await is disabled', function () { + const request = new Request(mainCollection.item[0].request); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('axios.request(config)'); + expect(snippet).to.include('.then((response) => {'); + expect(snippet).to.include('.catch((error) => {'); + }); + }); + + it('should return snippet with async/await based code when option is enabled', function () { + const request = new Request(mainCollection.item[0].request); + + convert(request, { asyncAwaitEnabled: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('async function makeRequest() {'); + expect(snippet).to.include('const response = await axios.request(config);'); + expect(snippet).to.include('catch (error) {'); + expect(snippet).to.include('makeRequest();'); + }); + }); + describe('getOptions function', function () { it('should return an array of specific options', function () { @@ -436,7 +518,7 @@ describe('nodejs-axios convert function', function () { expect(getOptions()[2]).to.have.property('id', 'requestTimeout'); expect(getOptions()[3]).to.have.property('id', 'followRedirect'); expect(getOptions()[4]).to.have.property('id', 'trimRequestBody'); - // expect(getOptions()[5]).to.have.property('id', 'AsyncAwait_enabled'); + expect(getOptions()[5]).to.have.property('id', 'asyncAwaitEnabled'); }); }); diff --git a/codegens/nodejs-native/.gitignore b/codegens/nodejs-native/.gitignore index 6f6e4e36b..541393f6e 100644 --- a/codegens/nodejs-native/.gitignore +++ b/codegens/nodejs-native/.gitignore @@ -20,6 +20,12 @@ pids .vscode *.sublime-* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Directory for instrumented libs generated by jscoverage/JSCover lib-cov diff --git a/codegens/nodejs-native/lib/request.js b/codegens/nodejs-native/lib/request.js index a281983ad..704088d11 100644 --- a/codegens/nodejs-native/lib/request.js +++ b/codegens/nodejs-native/lib/request.js @@ -1,5 +1,5 @@ const _ = require('./lodash'), - sdk = require('postman-collection'), + { Url } = require('postman-collection/lib/collection/url'), sanitizeOptions = require('./util').sanitizeOptions, sanitize = require('./util').sanitize, addFormParam = require('./util').addFormParam, @@ -132,7 +132,7 @@ function makeSnippet (request, indentString, options) { } - url = sdk.Url.parse(request.url.toString()); + url = Url.parse(request.url.toString()); host = url.host ? url.host.join('.') : ''; path = url.path ? '/' + url.path.join('/') : '/'; query = url.query ? _.reduce(url.query, (accum, q) => { diff --git a/codegens/nodejs-native/npm-shrinkwrap.json b/codegens/nodejs-native/npm-shrinkwrap.json index e7b844e75..ca70e495a 100644 --- a/codegens/nodejs-native/npm-shrinkwrap.json +++ b/codegens/nodejs-native/npm-shrinkwrap.json @@ -4,11 +4,116 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==" + }, + "charset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", + "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==" + }, + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==" + }, "follow-redirects": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", - "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true + }, + "http-reasons": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", + "integrity": "sha512-P6kYh0lKZ+y29T2Gqz+RlC9WBLhKe8kDmcJ+A+611jFfxdPsbMRQ5aNmFRM3lENqFkK+HTTL+tlQviAiv0AbLQ==" + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "liquid-json": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", + "integrity": "sha512-wUayTU8MS827Dam6MxgD72Ui+KOSF+u/eIqpatOtjnvgJ0+mnDq33uC2M7J0tPK+upe/DpUAuK4JUU89iBoNKQ==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-format": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.2.tgz", + "integrity": "sha512-Y5ERWVcyh3sby9Fx2U5F1yatiTFjNsqF5NltihTWI9QgNtr5o3dbCZdcKa1l2wyfhnwwoP9HGNxga7LqZLA6gw==", + "requires": { + "charset": "^1.0.0" + } + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "postman-collection": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-5.0.0.tgz", + "integrity": "sha512-1LK795Atv/ZX3jK1MCTx9KCBz0rAiIJJhTLqnJ4AsXLiLSqJuAH1w5jI1CQzHVLpPFg6E8Rl4tQIhF0eBgKNQQ==", + "requires": { + "@faker-js/faker": "5.5.3", + "file-type": "3.9.0", + "http-reasons": "0.1.0", + "iconv-lite": "0.6.3", + "liquid-json": "0.3.1", + "lodash": "4.17.21", + "mime-format": "2.0.2", + "mime-types": "2.1.35", + "postman-url-encoder": "3.0.6", + "semver": "7.7.1", + "uuid": "8.3.2" + } + }, + "postman-url-encoder": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.6.tgz", + "integrity": "sha512-uOlnZW+4Cmpbfbuq02hdj1hSpcIFmQxlAwsO6dflwUIVpt9+1duYVxXv3ikf+wHrAO8Wy98uVKnnuR8R0Qpdng==", + "requires": { + "punycode": "^2.3.1" + } + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } } diff --git a/codegens/nodejs-native/package.json b/codegens/nodejs-native/package.json index 4ee71eb60..d3eff68ed 100644 --- a/codegens/nodejs-native/package.json +++ b/codegens/nodejs-native/package.json @@ -27,10 +27,10 @@ "license": "Apache-2.0", "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/nodejs-native", "dependencies": { - "postman-collection": "3.6.11" + "postman-collection": "5.0.0" }, "devDependencies": { - "follow-redirects": "1.14.9" + "follow-redirects": "1.15.2" }, "engines": { "node": ">=8" diff --git a/codegens/nodejs-native/test/newman/newman.test.js b/codegens/nodejs-native/test/newman/newman.test.js index 77fa0174e..419b4564e 100644 --- a/codegens/nodejs-native/test/newman/newman.test.js +++ b/codegens/nodejs-native/test/newman/newman.test.js @@ -7,7 +7,8 @@ describe('Convert for different types of request', function () { headerSnippet: '/* eslint-disable */\n', compileScript: null, runScript: 'node run.js', - fileName: 'run.js' + fileName: 'run.js', + skipCollections: ['queryParamsCollection'] }; runNewmanTest(convert, options, testConfig); @@ -18,7 +19,8 @@ describe('Convert for different types of request', function () { compileScript: null, runScript: 'node run.js', fileName: 'run.js', - headerSnippet: '/* eslint-disable */\n' + headerSnippet: '/* eslint-disable */\n', + skipCollections: ['queryParamsCollection'] }; runNewmanTest(convert, options, testConfig); diff --git a/codegens/nodejs-native/test/unit/fixtures/testcollection/collection.json b/codegens/nodejs-native/test/unit/fixtures/testcollection/collection.json index 501fce45e..febbfd3e0 100644 --- a/codegens/nodejs-native/test/unit/fixtures/testcollection/collection.json +++ b/codegens/nodejs-native/test/unit/fixtures/testcollection/collection.json @@ -163,13 +163,11 @@ "header": [], "body": {}, "url": { - "raw": "https://89c918b1-f4f8-4812-8e6c-69ecbeeb8409.mock.pstmn.io?query1=1&query2=2", + "raw": "https://postman-echo.com?query1=1&query2=2", "protocol": "https", "host": [ - "89c918b1-f4f8-4812-8e6c-69ecbeeb8409", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [], "query": [ @@ -1051,14 +1049,12 @@ ], "body": {}, "url": { - "raw": "https://bf1621bb-f962-46b8-bf28-459e03b513ff.mock.pstmn.io/head", + "raw": "https://postman-echo.com/head", "protocol": "https", "host": [ - "bf1621bb-f962-46b8-bf28-459e03b513ff", - "mock", - "pstmn", - "io" - ], + "postman-echo", + "com" + ], "path": [ "head" ] @@ -1082,11 +1078,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1111,11 +1107,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1140,11 +1136,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1161,11 +1157,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1176,7 +1172,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1190,11 +1186,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1219,11 +1215,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1240,13 +1236,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1260,13 +1254,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1355,7 +1347,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1366,13 +1358,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1385,13 +1375,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1401,7 +1389,7 @@ "header": [], "cookie": [], "responseTime": "0", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] } diff --git a/codegens/nodejs-native/test/unit/snippet.test.js b/codegens/nodejs-native/test/unit/snippet.test.js index 9330554cb..b62edda0a 100644 --- a/codegens/nodejs-native/test/unit/snippet.test.js +++ b/codegens/nodejs-native/test/unit/snippet.test.js @@ -1,21 +1,19 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), convert = require('../../lib/index').convert; describe('nodejs-native convert function', function () { it('should sustain path variables when request has no path and has query params', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'body': {}, 'url': { - 'raw': 'https://89c918b1-f4f8-4812-8e6c-69ecbeeb8409.mock.pstmn.io?query1=1&query2=2', + 'raw': 'https://postman-echo.com?query1=1&query2=2', 'protocol': 'https', 'host': [ - '89c918b1-f4f8-4812-8e6c-69ecbeeb8409', - 'mock', - 'pstmn', - 'io' + 'postman-echo', + 'com' ], 'path': [], 'query': [ @@ -44,7 +42,7 @@ describe('nodejs-native convert function', function () { it('should parse the url correctly even if the host and path are wrong in the url object', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'body': { 'mode': 'raw', @@ -72,7 +70,7 @@ describe('nodejs-native convert function', function () { }); it('should add port in the options when host has port specified', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -98,7 +96,7 @@ describe('nodejs-native convert function', function () { }); it('should use JSON.parse if the content-type is application/vnd.api+json', function () { - let request = new sdk.Request({ + let request = new Request({ 'method': 'POST', 'header': [ { @@ -132,7 +130,7 @@ describe('nodejs-native convert function', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -159,7 +157,7 @@ describe('nodejs-native convert function', function () { }); it('should add content type if formdata field contains a content-type', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -194,7 +192,7 @@ describe('nodejs-native convert function', function () { }); it('should return snippet with ES6 features when ES6_enabled is set to true', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -238,7 +236,7 @@ describe('nodejs-native convert function', function () { }); it('should include JSON.stringify in the snippet for raw json bodies', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -271,7 +269,7 @@ describe('nodejs-native convert function', function () { }); }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -319,7 +317,7 @@ describe('nodejs-native convert function', function () { }); }); it('should generate valid snippet for single/double quotes in url', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -355,7 +353,7 @@ describe('nodejs-native convert function', function () { }); it('should generate valid snippet and should include appropriate variable name', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'body': {}, @@ -373,7 +371,7 @@ describe('nodejs-native convert function', function () { it('should generate valid snippet paths for single/double quotes in URL', function () { // url = https://a"b'c.com/'d/"e - var request = new sdk.Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes + var request = new Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes convert(request, {}, function (error, snippet) { if (error) { expect.fail(null, null, error); diff --git a/codegens/nodejs-request/.gitignore b/codegens/nodejs-request/.gitignore index dd41cd99b..f57ef32e8 100644 --- a/codegens/nodejs-request/.gitignore +++ b/codegens/nodejs-request/.gitignore @@ -12,6 +12,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # temporarily generated file run.js diff --git a/codegens/nodejs-request/test/unit/fixtures/testcollection/collection.json b/codegens/nodejs-request/test/unit/fixtures/testcollection/collection.json index 186aa9ce5..82e46b24f 100644 --- a/codegens/nodejs-request/test/unit/fixtures/testcollection/collection.json +++ b/codegens/nodejs-request/test/unit/fixtures/testcollection/collection.json @@ -991,13 +991,11 @@ ], "body": {}, "url": { - "raw": "https://bf1621bb-f962-46b8-bf28-459e03b513ff.mock.pstmn.io/head", + "raw": "https://postman-echo.com/head", "protocol": "https", "host": [ - "bf1621bb-f962-46b8-bf28-459e03b513ff", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path": [ "head" @@ -1022,11 +1020,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1051,11 +1049,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1080,11 +1078,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1101,11 +1099,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1130,11 +1128,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1159,11 +1157,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1180,13 +1178,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1200,13 +1196,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1295,7 +1289,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1306,13 +1300,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1325,13 +1317,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1341,7 +1331,7 @@ "header": [], "cookie": [], "responseTime": "0", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] } diff --git a/codegens/nodejs-request/test/unit/snippet.test.js b/codegens/nodejs-request/test/unit/snippet.test.js index 83a7891c7..e396f368d 100644 --- a/codegens/nodejs-request/test/unit/snippet.test.js +++ b/codegens/nodejs-request/test/unit/snippet.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), sanitize = require('../../lib/util').sanitize, parseBody = require('../../lib/parseRequest').parseBody, getOptions = require('../../lib/index').getOptions, @@ -15,7 +15,7 @@ describe('nodejs-request convert function', function () { line_no; it('should return a Tab indented snippet ', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { indentType: 'Tab', indentCount: 1 @@ -36,7 +36,7 @@ describe('nodejs-request convert function', function () { }); it('should use JSON.parse if the content-type is application/vnd.api+json', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [ { @@ -70,7 +70,7 @@ describe('nodejs-request convert function', function () { }); it('should return snippet with timeout property when timeout is set to non zero', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000 }; @@ -86,7 +86,7 @@ describe('nodejs-request convert function', function () { }); it('should return snippet with ES6 features when ES6_enabled is set to true', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { ES6_enabled: true }; @@ -106,7 +106,7 @@ describe('nodejs-request convert function', function () { it('should return snippet with followRedirect property set to ' + 'false for no follow redirect', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { followRedirect: false }; @@ -127,7 +127,7 @@ describe('nodejs-request convert function', function () { 'url': 'https://echo.getpostman.com/post', 'method': 'POST' }; - request = new sdk.Request(reqObject); + request = new Request(reqObject); convert(request, options, function (error, snippet) { if (error) { expect.fail(null, null, error); @@ -139,7 +139,7 @@ describe('nodejs-request convert function', function () { }); it('should not fail for a random body mode', function () { - request = new sdk.Request(mainCollection.item[2].request); + request = new Request(mainCollection.item[2].request); request.body.mode = 'random'; request.body[request.body.mode] = {}; @@ -155,7 +155,7 @@ describe('nodejs-request convert function', function () { }); it('should generate snippet for file body mode', function () { - request = new sdk.Request({ + request = new Request({ 'url': 'https://echo.getpostman.com/post', 'method': 'POST', 'body': { @@ -196,7 +196,7 @@ describe('nodejs-request convert function', function () { if (error) throw new Error(error); console.log(response.body); }); */ - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = {}; convert(request, options, function (error, snippet) { if (error) { @@ -220,7 +220,7 @@ describe('nodejs-request convert function', function () { it('should return snippet with no trailing comma when requestTimeout ' + 'is set to non zero and followRedirect as true', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000 }; @@ -238,7 +238,7 @@ describe('nodejs-request convert function', function () { it('should return snippet with just a single comma when requestTimeout ' + 'is set to non zero and followRedirect as false', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000, followRedirect: false, @@ -258,7 +258,7 @@ describe('nodejs-request convert function', function () { }); it('should not require unused fs', function () { - request = new sdk.Request({ + request = new Request({ 'url': 'https://postman-echo.com/get', 'method': 'GET', 'body': { @@ -276,7 +276,7 @@ describe('nodejs-request convert function', function () { }); it('should add fs for form-data file upload', function () { - request = new sdk.Request({ + request = new Request({ 'url': 'https://postman-echo.com/post', 'method': 'POST', 'body': { @@ -300,7 +300,7 @@ describe('nodejs-request convert function', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -327,7 +327,7 @@ describe('nodejs-request convert function', function () { }); it('should include JSON.stringify in the snippet for raw json bodies', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -361,7 +361,7 @@ describe('nodejs-request convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { diff --git a/codegens/nodejs-unirest/.gitignore b/codegens/nodejs-unirest/.gitignore index 3fa4c1b7b..28b01017f 100644 --- a/codegens/nodejs-unirest/.gitignore +++ b/codegens/nodejs-unirest/.gitignore @@ -12,6 +12,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/nodejs-unirest/test/newman/newman.test.js b/codegens/nodejs-unirest/test/newman/newman.test.js index 2e10a6591..4dd14610d 100644 --- a/codegens/nodejs-unirest/test/newman/newman.test.js +++ b/codegens/nodejs-unirest/test/newman/newman.test.js @@ -7,7 +7,8 @@ describe('Convert for different types of request', function () { compileScript: null, runScript: 'node run.js', fileName: 'run.js', - headerSnippet: '/* eslint-disable */\n' + headerSnippet: '/* eslint-disable */\n', + skipCollections: ['unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); @@ -18,7 +19,8 @@ describe('Convert for different types of request', function () { compileScript: null, runScript: 'node run.js', fileName: 'run.js', - headerSnippet: '/* eslint-disable */\n' + headerSnippet: '/* eslint-disable */\n', + skipCollections: ['unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); diff --git a/codegens/nodejs-unirest/test/unit/fixtures/testcollection/collection.json b/codegens/nodejs-unirest/test/unit/fixtures/testcollection/collection.json index 5af24b1f8..09590eacb 100644 --- a/codegens/nodejs-unirest/test/unit/fixtures/testcollection/collection.json +++ b/codegens/nodejs-unirest/test/unit/fixtures/testcollection/collection.json @@ -978,11 +978,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1006,11 +1006,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1034,11 +1034,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1054,11 +1054,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1082,11 +1082,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1101,11 +1101,11 @@ "method": "PURGE", "header": [], "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1121,11 +1121,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1141,11 +1141,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1257,14 +1257,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/nodejs-unirest/test/unit/snippet.test.js b/codegens/nodejs-unirest/test/unit/snippet.test.js index 733e637b1..84752fdbc 100644 --- a/codegens/nodejs-unirest/test/unit/snippet.test.js +++ b/codegens/nodejs-unirest/test/unit/snippet.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), convert = require('../../lib/index').convert, getOptions = require('../../lib/index').getOptions, sanitize = require('../../lib/util').sanitize, @@ -12,7 +12,7 @@ describe('nodejs unirest convert function', function () { var request, reqObject, options, snippetArray, line_no; it('should return a Tab indented snippet ', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { indentType: 'Tab', indentCount: 1 @@ -34,7 +34,7 @@ describe('nodejs unirest convert function', function () { }); it('should return snippet with timeout function when timeout is set to non zero', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000 }; @@ -49,7 +49,7 @@ describe('nodejs unirest convert function', function () { }); it('should use JSON.parse if the content-type is application/vnd.api+json', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [ { @@ -83,7 +83,7 @@ describe('nodejs unirest convert function', function () { }); it('should return snippet with ES6 features', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { ES6_enabled: true }; @@ -103,7 +103,7 @@ describe('nodejs unirest convert function', function () { it('should return snippet with followRedirect function having ' + 'parameter false for no follow redirect', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { followRedirect: false }; @@ -123,7 +123,7 @@ describe('nodejs unirest convert function', function () { 'url': 'https://echo.getpostman.com/post', 'method': 'POST' }; - request = new sdk.Request(reqObject); + request = new Request(reqObject); options = {}; convert(request, options, function (error, snippet) { if (error) { @@ -137,7 +137,7 @@ describe('nodejs unirest convert function', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -164,7 +164,7 @@ describe('nodejs unirest convert function', function () { }); it('should include JSON.stringify in the snippet for raw json bodies', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -198,7 +198,7 @@ describe('nodejs unirest convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -245,7 +245,7 @@ describe('nodejs unirest convert function', function () { }); it('should generate valid snippet for single/double quotes in url', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { diff --git a/codegens/objective-c/.gitignore b/codegens/objective-c/.gitignore index 909b2b654..2bc96af71 100644 --- a/codegens/objective-c/.gitignore +++ b/codegens/objective-c/.gitignore @@ -12,6 +12,12 @@ yarn-error.log* # Coverage directory used by tools like istanbul .coverage +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # node-waf configuration .lock-wscript diff --git a/codegens/objective-c/test/newman/newman.test.js b/codegens/objective-c/test/newman/newman.test.js index efa9c568c..79119945e 100644 --- a/codegens/objective-c/test/newman/newman.test.js +++ b/codegens/objective-c/test/newman/newman.test.js @@ -8,7 +8,8 @@ describe.skip('Convert for different types of request', function () { compileScript: 'clang -framework Foundation snippet.m -o prog', runScript: './prog', fileName: 'snippet.m', - headerSnippet: '' + headerSnippet: '', + skipCollections: ['queryParamsCollection'] }; runNewmanTest(convert, options, testConfig); diff --git a/codegens/objective-c/test/unit/convert.test.js b/codegens/objective-c/test/unit/convert.test.js index bd5f881d7..590f94b57 100644 --- a/codegens/objective-c/test/unit/convert.test.js +++ b/codegens/objective-c/test/unit/convert.test.js @@ -1,14 +1,14 @@ var convert = require('../../index').convert, expect = require('chai').expect, collection = require('./fixtures/collection.json'), - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), expectedSnippets = require('./fixtures/snippets.json'); describe('Objective-C-NSURLSession Converter', function () { describe('convert for different request types', function () { collection.item.forEach((item) => { it(item.name, function (done) { - const request = new sdk.Request(item.request); + const request = new Request(item.request); convert(request, {}, (err, snippet) => { if (err) { expect.fail(null, null, err); @@ -24,7 +24,7 @@ describe('Objective-C-NSURLSession Converter', function () { describe('Options Tests', function () { it('should indent snippet with type and count specified', function () { - var request = new sdk.Request(collection.item[0].request); + var request = new Request(collection.item[0].request); convert(request, { indentType: 'Tab', indentCount: 2 @@ -46,7 +46,7 @@ describe('Options Tests', function () { }); it('should use all the default options', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -84,7 +84,7 @@ describe('Options Tests', function () { }); it('should add code for requestTimeout option', function () { - var request = new sdk.Request(collection.item[0].request); + var request = new Request(collection.item[0].request); convert(request, { requestTimeout: 5000 }, function (error, snippet) { @@ -97,7 +97,7 @@ describe('Options Tests', function () { }); it('should trim request body when trimRequestBody is set to true', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -129,7 +129,7 @@ describe('Options Tests', function () { }); it('should include boiler plate if includeBoilerplate is set to true', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { diff --git a/codegens/objective-c/test/unit/fixtures/collection.json b/codegens/objective-c/test/unit/fixtures/collection.json index 336aee830..832f1007e 100644 --- a/codegens/objective-c/test/unit/fixtures/collection.json +++ b/codegens/objective-c/test/unit/fixtures/collection.json @@ -37,11 +37,11 @@ } ], "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -848,11 +848,11 @@ "method": "LINK", "header": [], "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -876,11 +876,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -904,11 +904,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -923,11 +923,11 @@ "method": "UNLOCK", "header": [], "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -951,11 +951,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -970,11 +970,11 @@ "method": "PURGE", "header": [], "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -989,11 +989,11 @@ "method": "COPY", "header": [], "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1007,13 +1007,11 @@ "method": "COPY", "header": [], "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, diff --git a/codegens/objective-c/test/unit/fixtures/snippets.json b/codegens/objective-c/test/unit/fixtures/snippets.json index f97ac73fa..1cd851a88 100644 --- a/codegens/objective-c/test/unit/fixtures/snippets.json +++ b/codegens/objective-c/test/unit/fixtures/snippets.json @@ -1,5 +1,5 @@ { - "Request Headers": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://mockbin.org/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"my-sample-header\": @\"Lorem ipsum dolor sit amet\",\n @\"testing\": @\"'singlequotes'\",\n @\"TEST\": @\"\\\"doublequotes\\\"\"\n};\n\n[request setAllHTTPHeaderFields:headers];\n\n[request setHTTPMethod:@\"POST\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", + "Request Headers": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"my-sample-header\": @\"Lorem ipsum dolor sit amet\",\n @\"testing\": @\"'singlequotes'\",\n @\"TEST\": @\"\\\"doublequotes\\\"\"\n};\n\n[request setAllHTTPHeaderFields:headers];\n\n[request setHTTPMethod:@\"POST\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", "Request Headers (With special Characters)": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/headers\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"my-sample-header\": @\"Lorem ipsum dolor sit amet\",\n @\"TEST\": @\"@#$%^&*()\",\n @\"more\": @\",./';[]}{\\\":?><|\\\\\\\\\"\n};\n\n[request setAllHTTPHeaderFields:headers];\n\n[request setHTTPMethod:@\"GET\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", "Request Headers with disabled headers": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/headers\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"my-sample-header\": @\"Lorem ipsum dolor sit amet\",\n @\"not-disabled-header\": @\"ENABLED\"\n};\n\n[request setAllHTTPHeaderFields:headers];\n\n[request setHTTPMethod:@\"GET\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", "GET Request with disabled query": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/get?test=123&anotherone=232\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"GET\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", @@ -14,13 +14,13 @@ "PATCH Request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/patch\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"PATCH\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", "DELETE Request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/delete\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Donec fermentum, nisi sed cursus eleifend, nulla tortor ultricies tellus, ut vehicula orci arcu ut velit. In volutpat egestas dapibus.\\nMorbi condimentum vestibulum sapien. Etiam dignissim diam quis eros lobortis gravida vel lobortis est. Etiam gravida sed.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"DELETE\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", "OPTIONS to postman echo": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/post\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"OPTIONS\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", - "LINK request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://mockbin.org/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"LINK\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", - "UNLINK request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://mockbin.org/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"UNLINK\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", - "LOCK request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://mockbin.org/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"LOCK\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", - "UNLOCK request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://mockbin.org/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"UNLOCK\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", - "PROPFIND request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://mockbin.org/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"PROPFIND\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", - "PURGE Request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://mockbin.org/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"PURGE\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", - "COPY Request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://mockbin.org/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"COPY\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", + "LINK request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"LINK\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", + "UNLINK request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"UNLINK\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", + "LOCK request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"LOCK\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", + "UNLOCK request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"UNLOCK\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", + "PROPFIND request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSDictionary *headers = @{\n @\"Content-Type\": @\"text/plain\"\n};\n\n[request setAllHTTPHeaderFields:headers];\nNSData *postData = [[NSData alloc] initWithData:[@\"Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.\" dataUsingEncoding:NSUTF8StringEncoding]];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"PROPFIND\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", + "PURGE Request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"PURGE\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", + "COPY Request": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/request\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\n\n[request setHTTPMethod:@\"COPY\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", "Post file": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/post\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSArray *parameters = @[\n @{ @\"name\": @\"uohou\", @\"fileName\": @\"/Users/umesh/Desktop/Screenshot 2019-09-29 at 10.50.30 AM.png\" } \n];\n\nNSString *boundary = @\"----WebKitFormBoundary7MA4YWxkTrZu0gW\";\nNSError *error;\nNSMutableString *body = [NSMutableString string];\n\nfor (NSDictionary *param in parameters) {\n [body appendFormat:@\"--%@\\r\\n\", boundary];\n if (param[@\"fileName\"]) {\n [body appendFormat:@\"Content-Disposition:form-data; name=\\\"%@\\\"; filename=\\\"%@\\\"\\r\\n\", param[@\"name\"], param[@\"fileName\"]];\n [body appendFormat:@\"Content-Type: %@\\r\\n\\r\\n\", param[@\"contentType\"]];\n [body appendFormat:@\"%@\", [NSString stringWithContentsOfFile:param[@\"fileName\"] encoding:NSUTF8StringEncoding error:&error]];\n if (error) {\n NSLog(@\"%@\", error);\n }\n } else {\n [body appendFormat:@\"Content-Disposition:form-data; name=\\\"%@\\\"\\r\\n\\r\\n\", param[@\"name\"]];\n [body appendFormat:@\"%@\", param[@\"value\"]];\n }\n}\n[body appendFormat:@\"\\r\\n--%@--\\r\\n\", boundary];\nNSData *postData = [body dataUsingEncoding:NSUTF8StringEncoding];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"POST\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", "Post multiple files in the same parameter via form-data": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/post\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSArray *parameters = @[\n @{ @\"name\": @\"multiple files\", @\"fileName\": @\"/Users/shreyshah/Desktop/openapi3.json\" }, \n @{ @\"name\": @\"multiple files\", @\"fileName\": @\"/Users/shreyshah/Desktop/openapi3.yaml\" } \n];\n\nNSString *boundary = @\"----WebKitFormBoundary7MA4YWxkTrZu0gW\";\nNSError *error;\nNSMutableString *body = [NSMutableString string];\n\nfor (NSDictionary *param in parameters) {\n [body appendFormat:@\"--%@\\r\\n\", boundary];\n if (param[@\"fileName\"]) {\n [body appendFormat:@\"Content-Disposition:form-data; name=\\\"%@\\\"; filename=\\\"%@\\\"\\r\\n\", param[@\"name\"], param[@\"fileName\"]];\n [body appendFormat:@\"Content-Type: %@\\r\\n\\r\\n\", param[@\"contentType\"]];\n [body appendFormat:@\"%@\", [NSString stringWithContentsOfFile:param[@\"fileName\"] encoding:NSUTF8StringEncoding error:&error]];\n if (error) {\n NSLog(@\"%@\", error);\n }\n } else {\n [body appendFormat:@\"Content-Disposition:form-data; name=\\\"%@\\\"\\r\\n\\r\\n\", param[@\"name\"]];\n [body appendFormat:@\"%@\", param[@\"value\"]];\n }\n}\n[body appendFormat:@\"\\r\\n--%@--\\r\\n\", boundary];\nNSData *postData = [body dataUsingEncoding:NSUTF8StringEncoding];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"POST\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", "Post a file via form-data, without file src specified": "#import \n\ndispatch_semaphore_t sema = dispatch_semaphore_create(0);\n\nNSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@\"https://postman-echo.com/post\"]\n cachePolicy:NSURLRequestUseProtocolCachePolicy\n timeoutInterval:10.0];\nNSArray *parameters = @[\n @{ @\"name\": @\"key\", @\"fileName\": @\"/path/to/file\" } \n];\n\nNSString *boundary = @\"----WebKitFormBoundary7MA4YWxkTrZu0gW\";\nNSError *error;\nNSMutableString *body = [NSMutableString string];\n\nfor (NSDictionary *param in parameters) {\n [body appendFormat:@\"--%@\\r\\n\", boundary];\n if (param[@\"fileName\"]) {\n [body appendFormat:@\"Content-Disposition:form-data; name=\\\"%@\\\"; filename=\\\"%@\\\"\\r\\n\", param[@\"name\"], param[@\"fileName\"]];\n [body appendFormat:@\"Content-Type: %@\\r\\n\\r\\n\", param[@\"contentType\"]];\n [body appendFormat:@\"%@\", [NSString stringWithContentsOfFile:param[@\"fileName\"] encoding:NSUTF8StringEncoding error:&error]];\n if (error) {\n NSLog(@\"%@\", error);\n }\n } else {\n [body appendFormat:@\"Content-Disposition:form-data; name=\\\"%@\\\"\\r\\n\\r\\n\", param[@\"name\"]];\n [body appendFormat:@\"%@\", param[@\"value\"]];\n }\n}\n[body appendFormat:@\"\\r\\n--%@--\\r\\n\", boundary];\nNSData *postData = [body dataUsingEncoding:NSUTF8StringEncoding];\n[request setHTTPBody:postData];\n\n[request setHTTPMethod:@\"POST\"];\n\nNSURLSession *session = [NSURLSession sharedSession];\nNSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request\ncompletionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {\n if (error) {\n NSLog(@\"%@\", error);\n dispatch_semaphore_signal(sema);\n } else {\n NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;\n NSError *parseError = nil;\n NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];\n NSLog(@\"%@\",responseDictionary);\n dispatch_semaphore_signal(sema);\n }\n}];\n[dataTask resume];\ndispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);", diff --git a/codegens/ocaml-cohttp/.gitignore b/codegens/ocaml-cohttp/.gitignore index 0b735f7ab..e23b590e7 100644 --- a/codegens/ocaml-cohttp/.gitignore +++ b/codegens/ocaml-cohttp/.gitignore @@ -12,6 +12,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/ocaml-cohttp/lib/ocaml.js b/codegens/ocaml-cohttp/lib/ocaml.js index 3eca2a4ef..93d233783 100644 --- a/codegens/ocaml-cohttp/lib/ocaml.js +++ b/codegens/ocaml-cohttp/lib/ocaml.js @@ -2,6 +2,7 @@ var _ = require('./lodash'), sanitize = require('./util').sanitize, sanitizeOptions = require('./util').sanitizeOptions, addFormParam = require('./util').addFormParam, + getUrlStringfromUrlObject = require('./util').getUrlStringfromUrlObject, self; /** @@ -313,7 +314,7 @@ self = module.exports = { // timeout = options.requestTimeout; // followRedirect = options.followRedirect; trim = options.trimRequestBody; - finalUrl = encodeURI(request.url.toString()); + finalUrl = getUrlStringfromUrlObject(request.url); methodArg = getMethodArg(request.method); if (request.body && !request.headers.has('Content-Type')) { if (request.body.mode === 'file') { diff --git a/codegens/ocaml-cohttp/lib/util.js b/codegens/ocaml-cohttp/lib/util.js index 0ca335940..e23d4c5be 100644 --- a/codegens/ocaml-cohttp/lib/util.js +++ b/codegens/ocaml-cohttp/lib/util.js @@ -1,4 +1,6 @@ -module.exports = { +const _ = require('./lodash'); + +const self = module.exports = { /** * sanitization of values : trim, escape characters * @@ -102,6 +104,91 @@ module.exports = { return result; }, + /** + * + * @param {Object} urlObject The request sdk request.url object + * @returns {String} The final string after parsing all the parameters of the url including + * protocol, auth, host, port, path, query, hash + * This will be used because the url.toString() method returned the URL with non encoded query string + * and hence a manual call is made to getQueryString() method with encode option set as true. + */ + getUrlStringfromUrlObject: function (urlObject) { + var url = ''; + if (!urlObject) { + return url; + } + if (urlObject.protocol) { + url += (urlObject.protocol.endsWith('://') ? urlObject.protocol : urlObject.protocol + '://'); + } + if (urlObject.auth && urlObject.auth.user) { + url = url + ((urlObject.auth.password) ? + urlObject.auth.user + ':' + urlObject.auth.password : urlObject.auth.user) + '@'; + } + if (urlObject.host) { + url += urlObject.getHost(); + } + if (urlObject.port) { + url += ':' + urlObject.port.toString(); + } + if (urlObject.path) { + url += urlObject.getPath(); + } + if (urlObject.query && urlObject.query.count()) { + let queryString = self.getQueryString(urlObject); + queryString && (url += '?' + queryString); + } + if (urlObject.hash) { + url += '#' + urlObject.hash; + } + + return self.sanitize(url, false); + }, + + /** + * @param {Object} urlObject + * @returns {String} + */ + getQueryString: function (urlObject) { + let isFirstParam = true, + params = _.get(urlObject, 'query.members'), + result = ''; + if (Array.isArray(params)) { + result = _.reduce(params, function (result, param) { + if (param.disabled === true) { + return result; + } + + if (isFirstParam) { + isFirstParam = false; + } + else { + result += '&'; + } + + return result + self.encodeParam(param.key) + '=' + self.encodeParam(param.value); + }, result); + } + + return result; + }, + + /** + * Encode param except the following characters- [,{,},],% + * + * @param {String} param + * @returns {String} + */ + encodeParam: function (param) { + return encodeURIComponent(param) + .replace(/%5B/g, '[') + .replace(/%7B/g, '{') + .replace(/%5D/g, ']') + .replace(/%7D/g, '}') + .replace(/%2B/g, '+') + .replace(/%25/g, '%') + .replace(/'/g, '%27'); + }, + /** * * @param {Array} array - form data array diff --git a/codegens/ocaml-cohttp/test/unit/convert.test.js b/codegens/ocaml-cohttp/test/unit/convert.test.js index 0d80296c8..6a89d0c6f 100644 --- a/codegens/ocaml-cohttp/test/unit/convert.test.js +++ b/codegens/ocaml-cohttp/test/unit/convert.test.js @@ -1,14 +1,16 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), convert = require('../../index').convert, getOptions = require('../../index').getOptions, sanitize = require('../../lib/util').sanitize, + getUrlStringfromUrlObject = require('../../lib/util').getUrlStringfromUrlObject, mainCollection = require('./fixtures/testcollection/collection.json'); describe('Ocaml unit tests', function () { describe('convert function', function () { - var request = new sdk.Request(mainCollection.item[0].request), + var request = new Request(mainCollection.item[0].request), snippetArray; const SINGLE_SPACE = ' '; @@ -63,7 +65,7 @@ describe('Ocaml unit tests', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -90,7 +92,7 @@ describe('Ocaml unit tests', function () { }); it('should add content type if formdata field contains a content-type', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -129,7 +131,7 @@ describe('Ocaml unit tests', function () { it('should include graphql body in the snippet', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -162,7 +164,7 @@ describe('Ocaml unit tests', function () { }); it('should generate snippets(not error out) for requests with multiple/no file in formdata', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -245,5 +247,15 @@ describe('Ocaml unit tests', function () { expect(sanitize(123, 'raw', false)).to.equal(''); expect(sanitize('inputString', 123, true)).to.equal('inputString'); }); + + it('should not encode unresolved query params and ' + + 'encode every other query param, both present together', function () { + let rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\'', + urlObject = new Url(rawUrl), + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b+c%27'); + }); }); }); diff --git a/codegens/ocaml-cohttp/test/unit/fixtures/testcollection/collection.json b/codegens/ocaml-cohttp/test/unit/fixtures/testcollection/collection.json index a298e0cb3..74ab9710f 100644 --- a/codegens/ocaml-cohttp/test/unit/fixtures/testcollection/collection.json +++ b/codegens/ocaml-cohttp/test/unit/fixtures/testcollection/collection.json @@ -52,11 +52,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -103,11 +103,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request?test=123&anotherone=232", + "raw": "https://postman-echo.com/request?test=123&anotherone=232", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -143,11 +143,11 @@ } ], "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -199,11 +199,11 @@ "raw": "\"'Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, \"id\" \"se\\\"mper\" sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.'\"" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -395,11 +395,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request?hardik=\"me\"", + "raw": "https://postman-echo.com/request?hardik=\"me\"", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -456,11 +456,11 @@ "raw": "{\n \"json\": \"Test-Test\"\n}" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -640,11 +640,11 @@ "raw": "var val = 6;\nconsole.log(val);" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -695,11 +695,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -750,11 +750,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -779,14 +779,13 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request/:action", + "raw": "https://postman-echo.com/:action", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ - "request", ":action" ], "variable": [ @@ -838,11 +837,11 @@ "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -891,11 +890,11 @@ "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -960,11 +959,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1015,11 +1014,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1045,11 +1044,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1073,11 +1072,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1101,11 +1100,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1129,11 +1128,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1152,11 +1151,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1177,11 +1176,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1205,11 +1204,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1228,13 +1227,11 @@ "raw": "" }, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1249,13 +1246,11 @@ "raw": "" }, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1343,7 +1338,7 @@ } ], "cookie": [], - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1357,11 +1352,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1379,11 +1374,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1495,14 +1490,14 @@ { "expires": "Invalid Date", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/php-curl/.gitignore b/codegens/php-curl/.gitignore index 62b6a2801..f9c9f5cf9 100644 --- a/codegens/php-curl/.gitignore +++ b/codegens/php-curl/.gitignore @@ -12,6 +12,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/php-curl/lib/php-curl.js b/codegens/php-curl/lib/php-curl.js index c6a24339a..a3c82d77e 100644 --- a/codegens/php-curl/lib/php-curl.js +++ b/codegens/php-curl/lib/php-curl.js @@ -3,6 +3,7 @@ var _ = require('./lodash'), sanitize = require('./util/sanitize').sanitize, sanitizeOptions = require('./util/sanitize').sanitizeOptions, addFormParam = require('./util/sanitize').addFormParam, + getUrlStringfromUrlObject = require('./util/sanitize').getUrlStringfromUrlObject, self; /** @@ -105,11 +106,7 @@ self = module.exports = { identity = options.indentType === 'Tab' ? '\t' : ' '; indentation = identity.repeat(options.indentCount); // concatenation and making up the final string - finalUrl = request.url.toString(); - if (finalUrl !== encodeURI(finalUrl)) { - // needs to be encoded - finalUrl = encodeURI(finalUrl); - } + finalUrl = getUrlStringfromUrlObject(request.url); snippet = ' '${sanitize(finalUrl, 'url')}',\n`; diff --git a/codegens/php-curl/lib/util/sanitize.js b/codegens/php-curl/lib/util/sanitize.js index 1aa6fe271..fab55ebee 100644 --- a/codegens/php-curl/lib/util/sanitize.js +++ b/codegens/php-curl/lib/util/sanitize.js @@ -1,4 +1,6 @@ -module.exports = { +const _ = require('../lodash'); + +const self = module.exports = { /** * sanitization of values : trim, escape characters * @@ -92,6 +94,91 @@ module.exports = { return result; }, + /** + * + * @param {Object} urlObject The request sdk request.url object + * @returns {String} The final string after parsing all the parameters of the url including + * protocol, auth, host, port, path, query, hash + * This will be used because the url.toString() method returned the URL with non encoded query string + * and hence a manual call is made to getQueryString() method with encode option set as true. + */ + getUrlStringfromUrlObject: function (urlObject) { + var url = ''; + if (!urlObject) { + return url; + } + if (urlObject.protocol) { + url += (urlObject.protocol.endsWith('://') ? urlObject.protocol : urlObject.protocol + '://'); + } + if (urlObject.auth && urlObject.auth.user) { + url = url + ((urlObject.auth.password) ? + urlObject.auth.user + ':' + urlObject.auth.password : urlObject.auth.user) + '@'; + } + if (urlObject.host) { + url += urlObject.getHost(); + } + if (urlObject.port) { + url += ':' + urlObject.port.toString(); + } + if (urlObject.path) { + url += urlObject.getPath(); + } + if (urlObject.query && urlObject.query.count()) { + let queryString = self.getQueryString(urlObject); + queryString && (url += '?' + queryString); + } + if (urlObject.hash) { + url += '#' + urlObject.hash; + } + + return self.sanitize(url, false); + }, + + /** + * @param {Object} urlObject + * @returns {String} + */ + getQueryString: function (urlObject) { + let isFirstParam = true, + params = _.get(urlObject, 'query.members'), + result = ''; + if (Array.isArray(params)) { + result = _.reduce(params, function (result, param) { + if (param.disabled === true) { + return result; + } + + if (isFirstParam) { + isFirstParam = false; + } + else { + result += '&'; + } + + return result + self.encodeParam(param.key) + '=' + self.encodeParam(param.value); + }, result); + } + + return result; + }, + + /** + * Encode param except the following characters- [,{,},],% + * + * @param {String} param + * @returns {String} + */ + encodeParam: function (param) { + return encodeURIComponent(param) + .replace(/%5B/g, '[') + .replace(/%7B/g, '{') + .replace(/%5D/g, ']') + .replace(/%7D/g, '}') + .replace(/%2B/g, '+') + .replace(/%25/g, '%') + .replace(/'/g, '%27'); + }, + /** * * @param {Array} array - form data array diff --git a/codegens/php-curl/test/unit/converter.test.js b/codegens/php-curl/test/unit/converter.test.js index 068b425fa..acbece8b9 100644 --- a/codegens/php-curl/test/unit/converter.test.js +++ b/codegens/php-curl/test/unit/converter.test.js @@ -1,6 +1,8 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), - convert = require('../../lib/index').convert; + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), + convert = require('../../lib/index').convert, + getUrlStringfromUrlObject = require('../../lib/util/sanitize').getUrlStringfromUrlObject; describe('php-curl converter', function () { it('should throw an error when callback is not function', function () { @@ -9,7 +11,7 @@ describe('php-curl converter', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -37,7 +39,7 @@ describe('php-curl converter', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -85,4 +87,13 @@ describe('php-curl converter', function () { }); }); + it('should not encode unresolved query params and ' + + 'encode every other query param, both present together', function () { + let rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\'', + urlObject = new Url(rawUrl), + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b+c%27'); + }); }); diff --git a/codegens/php-curl/test/unit/fixtures/sample_collection.json b/codegens/php-curl/test/unit/fixtures/sample_collection.json index d8a244a28..e6f6d6554 100644 --- a/codegens/php-curl/test/unit/fixtures/sample_collection.json +++ b/codegens/php-curl/test/unit/fixtures/sample_collection.json @@ -1142,11 +1142,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1171,11 +1171,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1200,11 +1200,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1221,11 +1221,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1250,11 +1250,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1279,11 +1279,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1300,13 +1300,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1320,13 +1318,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1340,13 +1336,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, diff --git a/codegens/php-guzzle/.gitignore b/codegens/php-guzzle/.gitignore index e4daa554f..66ebd66ed 100644 --- a/codegens/php-guzzle/.gitignore +++ b/codegens/php-guzzle/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Coverage directory used by tools like istanbul .coverage diff --git a/codegens/php-guzzle/lib/phpGuzzle.js b/codegens/php-guzzle/lib/phpGuzzle.js index 54173f448..9bc72f8e8 100644 --- a/codegens/php-guzzle/lib/phpGuzzle.js +++ b/codegens/php-guzzle/lib/phpGuzzle.js @@ -147,7 +147,7 @@ function getSnippetBoilerplate (includeBoilerplate) { 'use GuzzleHttp\\Psr7\\Utils;\n' + 'use GuzzleHttp\\Psr7\\Request;\n'; } - return ''; + return '', function () { - var collection = new sdk.Collection(JSON.parse( + var collection = new Collection(JSON.parse( fs.readFileSync('test/unit/fixtures/sample_collection.json').toString())), request = collection.items.members[0].request; convert(request, function (error, snippet) { @@ -469,7 +470,7 @@ describe('Request Snippet', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -505,11 +506,11 @@ describe('Request Snippet', function () { 'raw': {} }, 'url': { - 'raw': 'https://mockbin.org/request', + 'raw': 'https://postman-echo.com/request', 'protocol': 'https', 'host': [ - 'mockbin', - 'org' + 'postman-echo', + 'com' ], 'path': [ 'request' @@ -526,11 +527,11 @@ describe('Request Snippet', function () { 'header': [], 'body': {}, 'url': { - 'raw': 'https://mockbin.org/request', + 'raw': 'https://postman-echo.com/request', 'protocol': 'https', 'host': [ - 'mockbin', - 'org' + 'postman-echo', + 'com' ], 'path': [ 'request' @@ -549,11 +550,11 @@ describe('Request Snippet', function () { 'mode': 'lorem' }, 'url': { - 'raw': 'https://mockbin.org/request', + 'raw': 'https://postman-echo.com/request', 'protocol': 'https', 'host': [ - 'mockbin', - 'org' + 'postman-echo', + 'com' ], 'path': [ 'request' @@ -569,11 +570,11 @@ describe('Request Snippet', function () { 'method': 'POST', 'header': [], 'url': { - 'raw': 'https://mockbin.org/request', + 'raw': 'https://postman-echo.com/request', 'protocol': 'https', 'host': [ - 'mockbin', - 'org' + 'postman-echo', + 'com' ], 'path': [ 'request' @@ -585,7 +586,7 @@ describe('Request Snippet', function () { }); it('should include graphql body in the snippet', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -618,7 +619,7 @@ describe('Request Snippet', function () { }); it('should generate snippets(not error out) for requests with multiple/no file in formdata', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { diff --git a/codegens/php-pecl-http/test/unit/fixtures/sample_collection.json b/codegens/php-pecl-http/test/unit/fixtures/sample_collection.json index f17278245..5b2e05c48 100644 --- a/codegens/php-pecl-http/test/unit/fixtures/sample_collection.json +++ b/codegens/php-pecl-http/test/unit/fixtures/sample_collection.json @@ -1142,11 +1142,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1171,11 +1171,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1200,11 +1200,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1221,11 +1221,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1236,7 +1236,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1250,11 +1250,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1279,11 +1279,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1300,13 +1300,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1320,13 +1318,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1340,13 +1336,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, diff --git a/codegens/postman-cli/.gitignore b/codegens/postman-cli/.gitignore new file mode 100644 index 000000000..bfb38cf93 --- /dev/null +++ b/codegens/postman-cli/.gitignore @@ -0,0 +1,72 @@ +.DS_Store +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ diff --git a/codegens/postman-cli/.jsdoc-config.json b/codegens/postman-cli/.jsdoc-config.json new file mode 100644 index 000000000..90e6d5d44 --- /dev/null +++ b/codegens/postman-cli/.jsdoc-config.json @@ -0,0 +1,34 @@ +{ + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc", "closure"] + }, + "source": { + "include": [ ], + "includePattern": ".+\\.js(doc)?$", + "excludePattern": "(^|\\/|\\\\)_" + }, + + "plugins": [ + "plugins/markdown" + ], + + "templates": { + "cleverLinks": false, + "monospaceLinks": false, + "highlightTutorialCode" : true + }, + + "opts": { + "template": "./node_modules/postman-jsdoc-theme", + "encoding": "utf8", + "destination": "./out/docs", + "recurse": true, + "readme": "README.md" + }, + + "markdown": { + "parser": "gfm", + "hardwrap": false + } +} diff --git a/codegens/postman-cli/.npmignore b/codegens/postman-cli/.npmignore new file mode 100644 index 000000000..17156c3bc --- /dev/null +++ b/codegens/postman-cli/.npmignore @@ -0,0 +1,74 @@ +### NPM Specific: Disregard recursive project files +### =============================================== +/.editorconfig +/.gitmodules +/test + +### Borrowed from .gitignore +### ======================== + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ diff --git a/codegens/postman-cli/README.md b/codegens/postman-cli/README.md new file mode 100644 index 000000000..fa7df7b53 --- /dev/null +++ b/codegens/postman-cli/README.md @@ -0,0 +1,221 @@ +# codegen-postman-cli + +> Converts Postman-SDK Request into code snippet for Postman CLI. + +#### Prerequisites +To run Code-Gen, ensure that you have NodeJS >= v8. A copy of the NodeJS installable can be downloaded from https://nodejs.org/en/download/package-manager. + +## Using the Module +The module will expose an object which will have property `convert` which is the function for converting the Postman-SDK request to Postman CLI code snippet and `getOptions` function which returns an array of supported options. + +## Supported Features + +### Authentication +The codegen supports all authentication types available in Postman CLI: +- **Basic Auth**: Username and password authentication +- **Bearer Token**: Token-based authentication +- **Digest Auth**: Digest authentication with realm, nonce, qop, etc. +- **OAuth 1.0**: OAuth 1.0 authentication with consumer key, token, signatures +- **OAuth 2.0**: OAuth 2.0 with access tokens +- **API Key**: API key in header or query parameters +- **Hawk**: Hawk authentication +- **NTLM**: NTLM authentication with domain support + +### convert function +Convert function takes three parameters + +* `request` - Postman-SDK Request Object + +* `options` - options is an object which has following properties + * `multiLine` - Boolean denoting whether to split command across multiple lines + * `longFormat` - Boolean denoting whether to use long form options (--header instead of -H) + * `lineContinuationCharacter` - Character used to mark continuation of statement on next line (\\, ^, or `) + * `quoteType` - String denoting the quote type to use (single or double) for URL + * `requestTimeoutInSeconds` - Integer denoting time after which the request will timeout in seconds + * `followRedirect` - Boolean denoting whether to automatically follow HTTP redirects + * `followOriginalHttpMethod` - Boolean denoting whether to redirect with original HTTP method + * `maxRedirects` - Integer denoting maximum number of redirects to follow + * `trimRequestBody` - Boolean denoting whether to trim request body fields + * `quiet` - Boolean denoting whether to display requested data without extra output + * `indentType` - String denoting type of indentation for code snippet. eg: 'Space', 'Tab' + * `indentCount` - The number of indentation characters to add per code level + +* `callback` - callback function with first parameter as error and second parameter as string for code snippet + +##### Example: +```js +var request = new sdk.Request('www.google.com'), //using postman sdk to create request + options = { + indentCount: 3, + indentType: 'Space', + requestTimeoutInSeconds: 200, + trimRequestBody: true, + multiLine: true, + followRedirect: true, + followOriginalHttpMethod: false, + maxRedirects: 0, + longFormat: true, + lineContinuationCharacter: '\\', + quoteType: 'single', + quiet: false + }; +convert(request, options, function(error, snippet) { + if (error) { + // handle error + } + // handle snippet +}); +``` + +### Authentication Examples + +#### Basic Authentication +```js +var request = new sdk.Request({ + url: 'https://postman-echo.com/basic-auth', + method: 'GET', + auth: { + type: 'basic', + basic: [ + { key: 'username', value: 'postman' }, + { key: 'password', value: 'password' } + ] + } +}); + +convert(request, {}, function(error, snippet) { + console.log(snippet); + // Output: postman request 'https://postman-echo.com/basic-auth' --auth-basic-username 'postman' --auth-basic-password 'password' +}); +``` + +#### Bearer Token +```js +var request = new sdk.Request({ + url: 'https://api.example.com/data', + method: 'GET', + auth: { + type: 'bearer', + bearer: [ + { key: 'token', value: 'your-token-here' } + ] + } +}); + +convert(request, {}, function(error, snippet) { + console.log(snippet); + // Output: postman request 'https://api.example.com/data' --auth-bearer-token 'your-token-here' +}); +``` + +#### API Key +```js +var request = new sdk.Request({ + url: 'https://api.example.com/data', + method: 'GET', + auth: { + type: 'apikey', + apikey: [ + { key: 'key', value: 'X-API-Key' }, + { key: 'value', value: 'my-secret-key' }, + { key: 'in', value: 'header' } + ] + } +}); + +convert(request, {}, function(error, snippet) { + console.log(snippet); + // Output: postman request 'https://api.example.com/data' --auth-apikey-key 'X-API-Key' --auth-apikey-value 'my-secret-key' --auth-apikey-in 'header' +}); +``` + +### getOptions function + +This function returns a list of options supported by this codegen. + +#### Example +```js +var options = getOptions(); + +console.log(options); +// output +// [ +// { +// name: 'Generate multiline snippet', +// id: 'multiLine', +// type: 'boolean', +// default: true, +// description: 'Split Postman CLI command across multiple lines' +// }, +// { +// name: 'Use long form options', +// id: 'longFormat', +// type: 'boolean', +// default: true, +// description: 'Use the long form for Postman CLI options (--header instead of -H)' +// }, +// { +// name: 'Line continuation character', +// id: 'lineContinuationCharacter', +// availableOptions: ['\\', '^', '`'], +// type: 'enum', +// default: '\\', +// description: 'Set a character used to mark the continuation of a statement on the next line' +// }, +// { +// name: 'Quote Type', +// id: 'quoteType', +// availableOptions: ['single', 'double'], +// type: 'enum', +// default: 'single', +// description: 'String denoting the quote type to use (single or double) for URL' +// }, +// { +// name: 'Set request timeout (in seconds)', +// id: 'requestTimeoutInSeconds', +// type: 'positiveInteger', +// default: 0, +// description: 'Set number of seconds the request should wait for a response before timing out (use 0 for infinity)' +// }, +// { +// name: 'Follow redirects', +// id: 'followRedirect', +// type: 'boolean', +// default: true, +// description: 'Automatically follow HTTP redirects' +// }, +// { +// name: 'Follow original HTTP method', +// id: 'followOriginalHttpMethod', +// type: 'boolean', +// default: false, +// description: 'Redirect with the original HTTP method instead of the default behavior of redirecting with GET' +// }, +// { +// name: 'Maximum number of redirects', +// id: 'maxRedirects', +// type: 'positiveInteger', +// default: 0, +// description: 'Set the maximum number of redirects to follow, defaults to 0 (unlimited)' +// }, +// { +// name: 'Trim request body fields', +// id: 'trimRequestBody', +// type: 'boolean', +// default: false, +// description: 'Remove white space and additional lines that may affect the server\'s response' +// }, +// { +// name: 'Use Quiet Mode', +// id: 'quiet', +// type: 'boolean', +// default: false, +// description: 'Display the requested data without showing any extra output.' +// } +// ] +``` +### Guidelines for using generated snippet + +* Since Postman-SDK Request object doesn't provide complete path of the file, it needs to be manually inserted in case of uploading a file. + +* The generated snippet uses the `postman request` command from the Postman CLI. Make sure you have the Postman CLI installed to run the generated commands. diff --git a/codegens/postman-cli/index.js b/codegens/postman-cli/index.js new file mode 100644 index 000000000..bb0a047c4 --- /dev/null +++ b/codegens/postman-cli/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/codegens/postman-cli/lib/index.js b/codegens/postman-cli/lib/index.js new file mode 100644 index 000000000..2863bbce7 --- /dev/null +++ b/codegens/postman-cli/lib/index.js @@ -0,0 +1,453 @@ +const { + sanitize, + sanitizeOptions, + getUrlStringfromUrlObject, + addFormParam, + form, + shouldAddHttpMethod, + getAuthFlags + } = require('./util'), + _ = require('./lodash'); + +var self; + +/** + * Initialize options and extract values + * + * @param {Object} options - Sanitized options object + * @param {Object} request - The request object + * @returns {Object} Extracted option values + */ +function initializeOptions (options, request) { + const quoteType = options.quoteType === 'single' ? '\'' : '"', + url = getUrlStringfromUrlObject(request.url, quoteType); + + let indent; + if (options.multiLine) { + indent = options.indentType === 'Tab' ? '\t' : ' '; + indent = ' ' + options.lineContinuationCharacter + '\n' + indent.repeat(options.indentCount); // eslint-disable-line max-len + } + else { + indent = ' '; + } + + return { + redirect: options.followRedirect, + maxRedirects: options.maxRedirects, + timeout: options.requestTimeoutInSeconds, + multiLine: options.multiLine, + format: options.longFormat, + trim: options.trimRequestBody, + quiet: options.quiet, + debug: options.debug, + followOriginalHttpMethod: options.followOriginalHttpMethod, + quoteType, + url, + indent + }; +} + +/** + * Build the base command with method and URL + * + * @param {Object} request - The request object + * @param {Object} opts - Extracted options + * @param {Object} options - Original options object + * @returns {string} Base snippet + */ +function buildBaseCommand (request, opts, options) { + let snippet = 'postman request'; + + if (shouldAddHttpMethod(request, options)) { + snippet += ` ${request.method}`; + } + + snippet += ` ${opts.quoteType + opts.url + opts.quoteType}`; + + return snippet; +} + +/** + * Add quiet, debug and timeout flags to snippet + * + * @param {string} snippet - Current snippet + * @param {Object} opts - Extracted options + * @returns {string} Updated snippet + */ +function addQuietAndTimeout (snippet, opts) { + if (opts.quiet) { + snippet += `${opts.indent}${form('-q', opts.format)}`; + } + if (opts.debug) { + snippet += `${opts.indent}--debug`; + } + if (opts.timeout > 0) { + snippet += `${opts.indent}--timeout ${opts.timeout}`; + } + return snippet; +} + +/** + * Set default Content-Type header if needed + * + * @param {Object} request - The request object + */ +function setDefaultContentType (request) { + if (request.body && !request.headers.has('Content-Type')) { + if (request.body.mode === 'file') { + request.addHeader({ + key: 'Content-Type', + value: 'text/plain' + }); + } + else if (request.body.mode === 'graphql') { + request.addHeader({ + key: 'Content-Type', + value: 'application/json' + }); + } + } +} + +/** + * Add headers to snippet + * + * @param {string} snippet - Current snippet + * @param {Object} request - The request object + * @param {Object} opts - Extracted options + * @returns {string} Updated snippet + */ +function addHeaders (snippet, request, opts) { + let headersData = request.toJSON().header; + if (headersData) { + headersData = _.reject(headersData, 'disabled'); + _.forEach(headersData, (header) => { + if (!header.key) { + return; + } + snippet += opts.indent + + `${form('-H', opts.format)} ${opts.quoteType}${sanitize(header.key, true, opts.quoteType)}`; + snippet += `: ${sanitize(header.value, false, opts.quoteType)}${opts.quoteType}`; + }); + } + return snippet; +} + +/** + * Process formdata to handle multiple files + * + * @param {Object} request - The request object + */ +function processFormData (request) { + // The following code handles multiple files in the same formdata param. + // It removes the form data params where the src property is an array of filepath strings + // Splits that array into different form data params with src set as a single filepath string + if (request.body && request.body.mode === 'formdata') { + let formdata = request.body.formdata, + formdataArray = []; + formdata.members.forEach((param) => { + let key = param.key, + type = param.type, + disabled = param.disabled, + contentType = param.contentType; + if (type === 'file') { + if (typeof param.src !== 'string') { + if (Array.isArray(param.src) && param.src.length) { + param.src.forEach((filePath) => { + addFormParam(formdataArray, key, param.type, filePath, disabled, contentType); + }); + } + else { + addFormParam(formdataArray, key, param.type, '/path/to/file', disabled, contentType); + } + } + else { + addFormParam(formdataArray, key, param.type, param.src, disabled, contentType); + } + } + else { + addFormParam(formdataArray, key, param.type, param.value, disabled, contentType); + } + }); + request.body.update({ + mode: 'formdata', + formdata: formdataArray + }); + } +} + +/** + * Add raw body to snippet + * + * @param {string} snippet - Current snippet + * @param {Object} body - Body data + * @param {Object} opts - Extracted options + * @returns {string} Updated snippet + */ +function addRawBody (snippet, body, opts) { + let rawBody = body.raw.toString(), + sanitizedBody = sanitize(rawBody, opts.trim, opts.quoteType); + + if (!opts.multiLine) { + try { + sanitizedBody = JSON.stringify(JSON.parse(sanitizedBody)); + } + catch (e) { + // Do nothing + } + } + + snippet += opts.indent + `${form('-d', opts.format)} ${opts.quoteType}${sanitizedBody}${opts.quoteType}`; + + return snippet; +} + +/** + * Add formdata body to snippet + * + * @param {string} snippet - Current snippet + * @param {Object} body - Body data + * @param {Object} opts - Extracted options + * @returns {string} Updated snippet + */ +function addFormDataBody (snippet, body, opts) { + _.forEach(body.formdata, function (data) { + if (data.disabled) { + return; + } + + if (data.type === 'file') { + const sanitizedSrc = sanitize(data.src, opts.trim, opts.quoteType); + snippet += `${opts.indent}${form('-f', opts.format)} ` + + `${opts.quoteType}${sanitize(data.key, opts.trim, opts.quoteType)}=@${sanitizedSrc}`; + snippet += opts.quoteType; + } + else { + const sanitizedValue = sanitize(data.value, opts.trim, '"', true), + finalValue = sanitize(sanitizedValue, opts.trim, opts.quoteType, opts.quoteType === '"'); + snippet += `${opts.indent}${form('-f', opts.format)} ` + + `${opts.quoteType}${sanitize(data.key, opts.trim, opts.quoteType)}=${finalValue}`; + snippet += opts.quoteType; + } + }); + return snippet; +} + +/** + * Add body data to snippet based on body mode + * + * @param {string} snippet - Current snippet + * @param {Object} request - The request object + * @param {Object} opts - Extracted options + * @returns {string} Updated snippet + */ +function addBodyData (snippet, request, opts) { + if (!request.body) { + return snippet; + } + + const body = request.body.toJSON(); + + if (_.isEmpty(body)) { + return snippet; + } + + switch (body.mode) { + case 'raw': + snippet = addRawBody(snippet, body, opts); + break; + + case 'formdata': + snippet = addFormDataBody(snippet, body, opts); + break; + + case 'file': + snippet += opts.indent + form('-d', opts.format) + + ` ${opts.quoteType}@${sanitize(body[body.mode].src, opts.trim, opts.quoteType)}${opts.quoteType}`; + break; + + default: + snippet += `${opts.indent}${form('-d', opts.format)} ${opts.quoteType}${opts.quoteType}`; + } + + return snippet; +} + +/** + * Add authentication flags to snippet + * + * @param {string} snippet - Current snippet + * @param {Object} request - The request object + * @param {Object} opts - Extracted options + * @returns {string} Updated snippet + */ +function addAuthentication (snippet, request, opts) { + if (request.auth) { + snippet += getAuthFlags(request.auth, opts.quoteType, opts.indent); + } + return snippet; +} + +/** + * Add redirect options to snippet + * + * @param {string} snippet - Current snippet + * @param {Object} opts - Extracted options + * @returns {string} Updated snippet + */ +function addRedirectOptions (snippet, opts) { + if (!opts.redirect) { + snippet += `${opts.indent}--redirects-ignore`; + } + + if (opts.followOriginalHttpMethod) { + snippet += `${opts.indent}--redirects-follow-method`; + } + + if (opts.maxRedirects > 0) { + snippet += `${opts.indent}--redirects-max ${opts.maxRedirects}`; + } + + return snippet; +} + +self = module.exports = { + convert: function (request, options, callback) { + + if (!_.isFunction(callback)) { + throw new Error('Postman-CLI-Converter: callback is not valid function'); + } + options = sanitizeOptions(options, self.getOptions()); + + // Initialize options + const opts = initializeOptions(options, request); + + // Build base command + let snippet = buildBaseCommand(request, opts, options); + + // Add quiet and timeout flags + snippet = addQuietAndTimeout(snippet, opts); + + // Set default Content-Type if needed + setDefaultContentType(request); + + // Add headers + snippet = addHeaders(snippet, request, opts); + + // Process formdata + processFormData(request); + + // Add body data + snippet = addBodyData(snippet, request, opts); + + // Add authentication + snippet = addAuthentication(snippet, request, opts); + + // Add redirect options + snippet = addRedirectOptions(snippet, opts); + + callback(null, snippet); + }, + getOptions: function () { + return [ + { + name: 'Set indentation type', + id: 'indentType', + type: 'enum', + availableOptions: ['Tab', 'Space'], + default: 'Space', + description: 'Select the character used to indent lines of code' + }, + { + name: 'Set indentation count', + id: 'indentCount', + type: 'positiveInteger', + default: 2, + description: 'Set the number of indentation characters to add per code level' + }, + { + name: 'Generate multiline snippet', + id: 'multiLine', + type: 'boolean', + default: true, + description: 'Split cURL command across multiple lines' + }, + { + name: 'Use long form options', + id: 'longFormat', + type: 'boolean', + default: true, + description: 'Use the long form for cURL options (--header instead of -H)' + }, + { + name: 'Line continuation character', + id: 'lineContinuationCharacter', + availableOptions: ['\\', '^', '`'], + type: 'enum', + default: '\\', + description: 'Set a character used to mark the continuation of a statement on the next line ' + + '(generally, \\ for OSX/Linux, ^ for Windows cmd and ` for Powershell)' + }, + { + name: 'Quote Type', + id: 'quoteType', + availableOptions: ['single', 'double'], + type: 'enum', + default: 'single', + description: 'String denoting the quote type to use (single or double) for URL ' + + '(Use double quotes when running curl in cmd.exe and single quotes for the rest)' + }, + { + name: 'Set request timeout (in seconds)', + id: 'requestTimeoutInSeconds', + type: 'positiveInteger', + default: 0, + description: 'Set number of seconds the request should wait for a response before ' + + 'timing out (use 0 for infinity)' + }, + { + name: 'Follow redirects', + id: 'followRedirect', + type: 'boolean', + default: true, + description: 'Automatically follow HTTP redirects' + }, + { + name: 'Follow original HTTP method', + id: 'followOriginalHttpMethod', + type: 'boolean', + default: false, + description: 'Redirect with the original HTTP method instead of the default behavior of redirecting with GET' + }, + + { + name: 'Maximum number of redirects', + id: 'maxRedirects', + type: 'positiveInteger', + default: 0, + description: 'Set the maximum number of redirects to follow, defaults to 0 (unlimited)' + }, + { + name: 'Trim request body fields', + id: 'trimRequestBody', + type: 'boolean', + default: false, + description: 'Remove white space and additional lines that may affect the server\'s response' + }, + { + name: 'Use Quiet Mode', + id: 'quiet', + type: 'boolean', + default: false, + description: 'Display the requested data without showing any extra output.' + }, + { + name: 'Use Debug Mode', + id: 'debug', + type: 'boolean', + default: false, + description: 'Show detailed execution information including retry attempts, redirects, and timing breakdowns.' + } + ]; + } +}; diff --git a/codegens/nodejs-axios/lib/lodash.js b/codegens/postman-cli/lib/lodash.js similarity index 100% rename from codegens/nodejs-axios/lib/lodash.js rename to codegens/postman-cli/lib/lodash.js diff --git a/codegens/postman-cli/lib/util.js b/codegens/postman-cli/lib/util.js new file mode 100644 index 000000000..8604c3e1c --- /dev/null +++ b/codegens/postman-cli/lib/util.js @@ -0,0 +1,649 @@ +const _ = require('./lodash'); + +var self = module.exports = { + /** + * sanitizes input string by handling escape characters eg: converts '''' to '\'\'', (" to \" and \ to \\ ) + * and trim input if required + * + * @param {String} inputString + * @param {Boolean} [trim] - indicates whether to trim string or not + * @param {String} [quoteType] - indicates which quoteType has to be escaped + * @param {Boolean} [backSlash] - indicates whether to escape backslash(\\) + * @param {Boolean} [urlEncode] - indicates whether to url-encode inputString + * @returns {String} + */ + sanitize: function (inputString, trim, quoteType, backSlash = false, urlEncode = false) { + if (typeof inputString !== 'string') { + return ''; + } + + if (urlEncode) { + inputString = encodeURIComponent(inputString); + } + + if (backSlash) { + inputString = inputString.replace(/\\/g, '\\\\'); + } + + if (quoteType === '"') { + inputString = inputString.replace(/"/g, '\\"'); + // Escape backslash if double quote was already escaped before call to sanitize + inputString = inputString.replace(/(? { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + + for (id in options) { + if (options.hasOwnProperty(id)) { + if (defaultOptions[id] === undefined) { + continue; + } + switch (defaultOptions[id].type) { + case 'boolean': + if (typeof options[id] !== 'boolean') { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'positiveInteger': + if (typeof options[id] !== 'number' || options[id] < 0) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'enum': + if (!defaultOptions[id].availableOptions.includes(options[id])) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + default: + result[id] = options[id]; + } + } + } + + for (id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + if (result[id] === undefined) { + result[id] = defaultOptions[id].default; + } + } + } + return result; + }, + + /** + * Generates args required for NTLM authentication to happen + * + * @param {*} auth - The request sdk request.auth object + * @param {string} quoteType - user provided option to decide whether to use single or double quotes + * @param {string} format - user provided option to decide whether to use long format or not + * @returns {string} - The string to be added if NTLM auth is required + */ + getNtlmAuthInfo: function (auth, quoteType, format) { + const ntlmAuth = auth && auth.ntlm; + + if (!auth || auth.type !== 'ntlm' || !ntlmAuth || !ntlmAuth.count || !ntlmAuth.count()) { + return ''; + } + + const username = ntlmAuth.has('username') && ntlmAuth.get('username'), + password = ntlmAuth.has('password') && ntlmAuth.get('password'), + domain = ntlmAuth.has('domain') && ntlmAuth.get('domain'); + + if (!username && !password) { + return ''; + } + + var userArg = format ? '--user ' : '-u ', + ntlmString = ' --ntlm ' + userArg + quoteType; + + if (domain) { + ntlmString += self.sanitize(domain, true, quoteType) + '\\'; + } + ntlmString += self.sanitize(username, true, quoteType) + ':' + self.sanitize(password, true, quoteType); + ntlmString += quoteType; + + return ntlmString; + }, + + /** + * + * @param {*} urlObject The request sdk request.url object + * @param {boolean} quoteType The user given quoteType + * @returns {String} The final string after parsing all the parameters of the url including + * protocol, auth, host, port, path, query, hash + * This will be used because the url.toString() method returned the URL with non encoded query string + * and hence a manual call is made to getQueryString() method with encode option set as true. + */ + getUrlStringfromUrlObject: function (urlObject, quoteType) { + var url = ''; + if (!urlObject) { + return url; + } + if (urlObject.protocol) { + url += (urlObject.protocol.endsWith('://') ? urlObject.protocol : urlObject.protocol + '://'); + } + if (urlObject.auth && urlObject.auth.user) { + url = url + ((urlObject.auth.password) ? + // ==> username:password@ + urlObject.auth.user + ':' + urlObject.auth.password : urlObject.auth.user) + '@'; + } + if (urlObject.host) { + url += urlObject.getHost(); + } + if (urlObject.port) { + url += ':' + urlObject.port.toString(); + } + if (urlObject.path) { + url += urlObject.getPath(); + } + if (urlObject.query && urlObject.query.count()) { + let queryString = self.getQueryString(urlObject); + queryString && (url += '?' + queryString); + } + if (urlObject.hash) { + url += '#' + urlObject.hash; + } + + return self.sanitize(url, false, quoteType); + }, + + /** + * @param {Object} urlObject + * @returns {String} + */ + getQueryString: function (urlObject) { + let isFirstParam = true, + params = _.get(urlObject, 'query.members'), + result = ''; + if (Array.isArray(params)) { + result = _.reduce(params, function (result, param) { + if (param.disabled === true) { + return result; + } + + if (isFirstParam) { + isFirstParam = false; + } + else { + result += '&'; + } + + return result + self.encodeParam(param.key) + '=' + self.encodeParam(param.value); + }, result); + } + + return result; + }, + + /** + * Encode param except the following characters- [,{,},],%,+ + * + * @param {String} param + * @returns {String} + */ + encodeParam: function (param) { + return encodeURIComponent(param) + .replace(/%5B/g, '[') + .replace(/%7B/g, '{') + .replace(/%5D/g, ']') + .replace(/%7D/g, '}') + .replace(/%2B/g, '+') + .replace(/%25/g, '%') + .replace(/'/g, '%27'); + }, + + /** + * + * @param {Array} array - form data array + * @param {String} key - key of form data param + * @param {String} type - type of form data param(file/text) + * @param {String} val - value/src property of form data param + * @param {String} disabled - Boolean denoting whether the param is disabled or not + * @param {String} contentType - content type header of the param + * + * Appends a single param to form data array + */ + addFormParam: function (array, key, type, val, disabled, contentType) { + if (type === 'file') { + array.push({ + key: key, + type: type, + src: val, + disabled: disabled, + contentType: contentType + }); + } + else { + array.push({ + key: key, + type: type, + value: val, + disabled: disabled, + contentType: contentType + }); + } + }, + + /** + * @param {Object} body + * @returns {boolean} + * + * Determines if a request body is actually empty. + * This is needed because body.isEmpty() returns false for formdata + * and urlencoded when they contain only disabled params which will not + * be a part of the CLI request. + */ + isBodyEmpty (body) { + if (!body) { + return true; + } + + if (body.isEmpty()) { + return true; + } + + if (body.mode === 'formdata' || body.mode === 'urlencoded') { + let memberCount = 0; + body[body.mode] && body[body.mode].members && body[body.mode].members.forEach((param) => { + if (!param.disabled) { + memberCount += 1; + } + }); + + return memberCount === 0; + } + + return false; + }, + + /** + * Generates authentication flags for Postman CLI based on request auth configuration + * + * @param {Object} auth - The request.auth object from Postman SDK + * @param {String} quoteType - User provided option to decide whether to use single or double quotes + * @param {String} indent - Indentation string + * @returns {String} - The authentication flags to be added to the CLI command + */ + getAuthFlags: function (auth, quoteType, indent) { + if (!auth || !auth.type) { + return ''; + } + + var authType = auth.type, + authData = auth[authType], + authFlags = '', + getAuthParam = function (paramName) { + if (!authData || !authData.members) { + return ''; + } + var param = authData.members.find(function (item) { return item.key === paramName; }); + return param ? param.value : ''; + }, + username, password, realm, nonce, algorithm, qop, nc, cnonce, opaque, token, tokenSecret, + consumerKey, consumerSecret, signatureMethod, timestamp, version, addParamsToHeader, + addEmptyParamsToSign, accessToken, addTokenTo, authId, authKey, user, extraData, app, + delegation, domain, workstation, key, value, inParam; + + switch (authType) { + case 'basic': + username = getAuthParam('username'); + password = getAuthParam('password'); + if (username || password) { + authFlags += indent + '--auth-basic-username ' + quoteType + + self.sanitize(username, true, quoteType) + quoteType; + authFlags += indent + '--auth-basic-password ' + quoteType + + self.sanitize(password, true, quoteType) + quoteType; + } + break; + + case 'bearer': + token = getAuthParam('token'); + if (token) { + authFlags += indent + '--auth-bearer-token ' + quoteType + + self.sanitize(token, true, quoteType) + quoteType; + } + break; + + case 'digest': + username = getAuthParam('username'); + password = getAuthParam('password'); + realm = getAuthParam('realm'); + nonce = getAuthParam('nonce'); + algorithm = getAuthParam('algorithm'); + qop = getAuthParam('qop'); + nc = getAuthParam('nc'); + cnonce = getAuthParam('cnonce'); + opaque = getAuthParam('opaque'); + + if (username) { + authFlags += indent + '--auth-digest-username ' + quoteType + + self.sanitize(username, true, quoteType) + quoteType; + } + if (password) { + authFlags += indent + '--auth-digest-password ' + quoteType + + self.sanitize(password, true, quoteType) + quoteType; + } + if (realm) { + authFlags += indent + '--auth-digest-realm ' + quoteType + + self.sanitize(realm, true, quoteType) + quoteType; + } + if (nonce) { + authFlags += indent + '--auth-digest-nonce ' + quoteType + + self.sanitize(nonce, true, quoteType) + quoteType; + } + if (algorithm) { + authFlags += indent + '--auth-digest-algorithm ' + quoteType + + self.sanitize(algorithm, true, quoteType) + quoteType; + } + if (qop) { + authFlags += indent + '--auth-digest-qop ' + quoteType + + self.sanitize(qop, true, quoteType) + quoteType; + } + if (nc) { + authFlags += indent + '--auth-digest-nc ' + quoteType + + self.sanitize(nc, true, quoteType) + quoteType; + } + if (cnonce) { + authFlags += indent + '--auth-digest-cnonce ' + quoteType + + self.sanitize(cnonce, true, quoteType) + quoteType; + } + if (opaque) { + authFlags += indent + '--auth-digest-opaque ' + quoteType + + self.sanitize(opaque, true, quoteType) + quoteType; + } + break; + + case 'oauth1': + consumerKey = getAuthParam('consumerKey'); + consumerSecret = getAuthParam('consumerSecret'); + token = getAuthParam('token'); + tokenSecret = getAuthParam('tokenSecret'); + signatureMethod = getAuthParam('signatureMethod'); + timestamp = getAuthParam('timestamp'); + nonce = getAuthParam('nonce'); + version = getAuthParam('version'); + realm = getAuthParam('realm'); + addParamsToHeader = getAuthParam('addParamsToHeader'); + addEmptyParamsToSign = getAuthParam('addEmptyParamsToSign'); + + if (consumerKey) { + authFlags += indent + '--auth-oauth1-consumerKey ' + quoteType + + self.sanitize(consumerKey, true, quoteType) + quoteType; + } + if (consumerSecret) { + authFlags += indent + '--auth-oauth1-consumerSecret ' + quoteType + + self.sanitize(consumerSecret, true, quoteType) + quoteType; + } + if (token) { + authFlags += indent + '--auth-oauth1-token ' + quoteType + + self.sanitize(token, true, quoteType) + quoteType; + } + if (tokenSecret) { + authFlags += indent + '--auth-oauth1-tokenSecret ' + quoteType + + self.sanitize(tokenSecret, true, quoteType) + quoteType; + } + if (signatureMethod) { + authFlags += indent + '--auth-oauth1-signatureMethod ' + quoteType + + self.sanitize(signatureMethod, true, quoteType) + quoteType; + } + if (timestamp) { + authFlags += indent + '--auth-oauth1-timestamp ' + quoteType + + self.sanitize(timestamp, true, quoteType) + quoteType; + } + if (nonce) { + authFlags += indent + '--auth-oauth1-nonce ' + quoteType + + self.sanitize(nonce, true, quoteType) + quoteType; + } + if (version) { + authFlags += indent + '--auth-oauth1-version ' + quoteType + + self.sanitize(version, true, quoteType) + quoteType; + } + if (realm) { + authFlags += indent + '--auth-oauth1-realm ' + quoteType + + self.sanitize(realm, true, quoteType) + quoteType; + } + if (addParamsToHeader) { + authFlags += indent + '--auth-oauth1-addParamsToHeader ' + quoteType + + self.sanitize(addParamsToHeader, true, quoteType) + quoteType; + } + if (addEmptyParamsToSign) { + authFlags += indent + '--auth-oauth1-addEmptyParamsToSign ' + quoteType + + self.sanitize(addEmptyParamsToSign, true, quoteType) + quoteType; + } + break; + + case 'oauth2': + accessToken = getAuthParam('accessToken'); + addTokenTo = getAuthParam('addTokenTo'); + + if (accessToken) { + authFlags += indent + '--auth-oauth2-accessToken ' + quoteType + + self.sanitize(accessToken, true, quoteType) + quoteType; + } + if (addTokenTo) { + authFlags += indent + '--auth-oauth2-addTokenTo ' + quoteType + + self.sanitize(addTokenTo, true, quoteType) + quoteType; + } + break; + + case 'hawk': + authId = getAuthParam('authId'); + authKey = getAuthParam('authKey'); + algorithm = getAuthParam('algorithm'); + user = getAuthParam('user'); + nonce = getAuthParam('nonce'); + extraData = getAuthParam('extraData'); + app = getAuthParam('app'); + delegation = getAuthParam('delegation'); + timestamp = getAuthParam('timestamp'); + + if (authId) { + authFlags += indent + '--auth-hawk-authId ' + quoteType + + self.sanitize(authId, true, quoteType) + quoteType; + } + if (authKey) { + authFlags += indent + '--auth-hawk-authKey ' + quoteType + + self.sanitize(authKey, true, quoteType) + quoteType; + } + if (algorithm) { + authFlags += indent + '--auth-hawk-algorithm ' + quoteType + + self.sanitize(algorithm, true, quoteType) + quoteType; + } + if (user) { + authFlags += indent + '--auth-hawk-user ' + quoteType + + self.sanitize(user, true, quoteType) + quoteType; + } + if (nonce) { + authFlags += indent + '--auth-hawk-nonce ' + quoteType + + self.sanitize(nonce, true, quoteType) + quoteType; + } + if (extraData) { + authFlags += indent + '--auth-hawk-extraData ' + quoteType + + self.sanitize(extraData, true, quoteType) + quoteType; + } + if (app) { + authFlags += indent + '--auth-hawk-app ' + quoteType + + self.sanitize(app, true, quoteType) + quoteType; + } + if (delegation) { + authFlags += indent + '--auth-hawk-delegation ' + quoteType + + self.sanitize(delegation, true, quoteType) + quoteType; + } + if (timestamp) { + authFlags += indent + '--auth-hawk-timestamp ' + quoteType + + self.sanitize(timestamp, true, quoteType) + quoteType; + } + break; + + case 'ntlm': + username = getAuthParam('username'); + password = getAuthParam('password'); + domain = getAuthParam('domain'); + workstation = getAuthParam('workstation'); + + if (username) { + authFlags += indent + '--auth-ntlm-username ' + quoteType + + self.sanitize(username, true, quoteType) + quoteType; + } + if (password) { + authFlags += indent + '--auth-ntlm-password ' + quoteType + + self.sanitize(password, true, quoteType) + quoteType; + } + if (domain) { + authFlags += indent + '--auth-ntlm-domain ' + quoteType + + self.sanitize(domain, true, quoteType) + quoteType; + } + if (workstation) { + authFlags += indent + '--auth-ntlm-workstation ' + quoteType + + self.sanitize(workstation, true, quoteType) + quoteType; + } + break; + + case 'apikey': + key = getAuthParam('key'); + value = getAuthParam('value'); + inParam = getAuthParam('in'); + + if (key) { + authFlags += indent + '--auth-apikey-key ' + quoteType + + self.sanitize(key, true, quoteType) + quoteType; + } + if (value) { + authFlags += indent + '--auth-apikey-value ' + quoteType + + self.sanitize(value, true, quoteType) + quoteType; + } + if (inParam) { + authFlags += indent + '--auth-apikey-in ' + quoteType + + self.sanitize(inParam, true, quoteType) + quoteType; + } + break; + + default: + // Unsupported auth type, return empty string + break; + } + + return authFlags; + }, + + + /** + * Decide whether we should add the HTTP method explicitly to the Postman CLI command. + * + * @param {Object} request + * + * @returns {Boolean} + */ + shouldAddHttpMethod: function (request) { + + if (request.method === 'GET') { + return false; + } + + return true; + // let followRedirect = options.followRedirect, + // followOriginalHttpMethod = options.followOriginalHttpMethod, + // disableBodyPruning = true, + // isBodyEmpty = self.isBodyEmpty(request.body); + + // // eslint-disable-next-line lodash/prefer-is-nil + // if (request.protocolProfileBehavior !== null && request.protocolProfileBehavior !== undefined) { + // followRedirect = _.get(request, 'protocolProfileBehavior.followRedirects', followRedirect); + // followOriginalHttpMethod = + // _.get(request, 'protocolProfileBehavior.followOriginalHttpMethod', followOriginalHttpMethod); + // disableBodyPruning = _.get(request, 'protocolProfileBehavior.disableBodyPruning', true); + // } + + // if (followRedirect && followOriginalHttpMethod) { + // return true; + // } + + // switch (request.method) { + // case 'HEAD': + // return false; + // case 'GET': + // // disableBodyPruning will generally not be present in the request + // // the only time it will be present, its value will be _false_ + // // i.e. the user wants to prune the request body despite it being present + // if (!isBodyEmpty && disableBodyPruning) { + // return true; + // } + + // return false; + // case 'POST': + // return isBodyEmpty; + // case 'DELETE': + // case 'PUT': + // case 'PATCH': + // default: + // return true; + // } + } +}; diff --git a/codegens/postman-cli/npm-shrinkwrap.json b/codegens/postman-cli/npm-shrinkwrap.json new file mode 100644 index 000000000..d8e84ce5c --- /dev/null +++ b/codegens/postman-cli/npm-shrinkwrap.json @@ -0,0 +1,5 @@ +{ + "name": "@postman/codegen-postman-cli", + "version": "0.0.1", + "lockfileVersion": 1 +} diff --git a/codegens/postman-cli/npm/test-lint.js b/codegens/postman-cli/npm/test-lint.js new file mode 100644 index 000000000..2f4db0cb8 --- /dev/null +++ b/codegens/postman-cli/npm/test-lint.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +var shell = require('shelljs'), + chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/postman-cli/npm/test-newman.js b/codegens/postman-cli/npm/test-newman.js new file mode 100644 index 000000000..ae7d2afe1 --- /dev/null +++ b/codegens/postman-cli/npm/test-newman.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all newman tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'newman'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running newman tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/postman-cli/npm/test-unit.js b/codegens/postman-cli/npm/test-unit.js new file mode 100755 index 000000000..0de7fd529 --- /dev/null +++ b/codegens/postman-cli/npm/test-unit.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running unit tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/postman-cli/npm/test.js b/codegens/postman-cli/npm/test.js new file mode 100755 index 000000000..2c454fb5d --- /dev/null +++ b/codegens/postman-cli/npm/test.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node +var chalk = require('chalk'), + exit = require('shelljs').exit, + prettyms = require('pretty-ms'), + startedAt = Date.now(), + name = require('../package.json').name; + +require('async').series([ + require('./test-lint'), + require('./test-unit'), + require('./test-newman') +], function (code) { + // eslint-disable-next-line max-len + console.info(chalk[code ? 'red' : 'green'](`\n${name}: duration ${prettyms(Date.now() - startedAt)}\n${name}: ${code ? 'not ok' : 'ok'}!`)); + exit(code && (typeof code === 'number' ? code : 1) || 0); +}); diff --git a/codegens/postman-cli/package.json b/codegens/postman-cli/package.json new file mode 100644 index 000000000..2b21eb4cf --- /dev/null +++ b/codegens/postman-cli/package.json @@ -0,0 +1,29 @@ +{ + "name": "@postman/codegen-postman-cli", + "version": "0.1.0", + "description": "Converter plugin to convert from postman sdk request to Postman CLI code snippet", + "com_postman_plugin": { + "type": "code_generator", + "lang": "postman-cli", + "variant": "Postman CLI", + "syntax_mode": "powershell" + }, + "main": "index.js", + "scripts": { + "test": "node npm/test.js", + "test-lint": "node npm/test-lint.js", + "test-unit": "node npm/test-unit.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "Postman Labs ", + "license": "Apache-2.0", + "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/postman-cli", + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=8" + } +} diff --git a/codegens/postman-cli/test/.eslintrc b/codegens/postman-cli/test/.eslintrc new file mode 100644 index 000000000..e8db1afa8 --- /dev/null +++ b/codegens/postman-cli/test/.eslintrc @@ -0,0 +1,29 @@ +{ + "plugins": [ + "mocha" + ], + "env": { + "mocha": true, + "node": true, + "es6": true + }, + "rules": { + "mocha/handle-done-callback": "error", + "mocha/max-top-level-suites": "error", + "mocha/no-exclusive-tests": "error", + "mocha/no-global-tests": "error", + "mocha/no-hooks-for-single-case": "off", + "mocha/no-hooks": "off", + "mocha/no-identical-title": "error", + "mocha/no-mocha-arrows": "error", + "mocha/no-nested-tests": "error", + "mocha/no-pending-tests": "error", + "mocha/no-return-and-callback": "error", + "mocha/no-sibling-hooks": "error", + "mocha/no-skipped-tests": "warn", + "mocha/no-synchronous-tests": "off", + "mocha/no-top-level-hooks": "warn", + "mocha/valid-test-description": "off", + "mocha/valid-suite-description": "off" + } +} diff --git a/codegens/postman-cli/test/ci-install.sh b/codegens/postman-cli/test/ci-install.sh new file mode 100755 index 000000000..0be0c8c74 --- /dev/null +++ b/codegens/postman-cli/test/ci-install.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/postman-cli" + +# Install curl command-line tool (needed to download Postman CLI) +echo "Installing curl..." +sudo apt-get update +sudo apt-get install -y curl + +# Install Postman CLI +echo "Installing Postman CLI..." +curl -o- "https://dl-cli.pstmn.io/install/linux64.sh" | sh diff --git a/codegens/postman-cli/test/newman/newman.test.js b/codegens/postman-cli/test/newman/newman.test.js new file mode 100644 index 000000000..f8f6304c4 --- /dev/null +++ b/codegens/postman-cli/test/newman/newman.test.js @@ -0,0 +1,32 @@ +var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, + convert = require('../../index').convert; + +describe('Convert for different types of request', function () { + var testConfig = {compileScript: null, runScript: null, fileName: null}, + options1 = { + indentCount: 3, + indentType: 'Space', + requestTimeoutInSeconds: 200, + multiLine: true, + followRedirect: true, + longFormat: true, + quiet: true, + lineContinuationCharacter: '\\', + quoteType: 'single' + }, + options2 = { + indentCount: 3, + indentType: 'Space', + requestTimeoutInSeconds: 200, + multiLine: true, + followRedirect: true, + longFormat: false, + quiet: true, + lineContinuationCharacter: '\\', + quoteType: 'single' + }; + + runNewmanTest(convert, options1, testConfig); + runNewmanTest(convert, options2, testConfig); +}); + diff --git a/codegens/postman-cli/test/unit/.gitkeep b/codegens/postman-cli/test/unit/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/codegens/postman-cli/test/unit/convert.test.js b/codegens/postman-cli/test/unit/convert.test.js new file mode 100644 index 000000000..f81b0f174 --- /dev/null +++ b/codegens/postman-cli/test/unit/convert.test.js @@ -0,0 +1,1455 @@ +var _ = require('lodash'), + expect = require('chai').expect, + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), + convert = require('../../index').convert, + getUrlStringfromUrlObject = require('../../lib/util').getUrlStringfromUrlObject; + +describe('postman-cli convert function', function () { + describe('Convert function', function () { + var request, options, snippetArray, line; + + it('should return snippet with carat(^) as line continuation ' + + 'character for multiline code generation', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '' + } + }); + options = { + multiLine: true, + lineContinuationCharacter: '^' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + snippetArray = snippet.split('\n'); + // Ignoring the last line as there is no line continuation character at last line + for (var i = 0; i < snippetArray.length - 1; i++) { + line = snippetArray[i]; + expect(line.charAt(line.length - 1)).to.equal('^'); + } + }); + }); + + it('should return snippet with url in single quote(\')', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + quoteType: 'single' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).to.contain('\'https://postman-echo.com/post\''); + }); + }); + + it('should return snippet with url in double quote(")', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + quoteType: 'double' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).to.contain('"https://postman-echo.com/post"'); + }); + }); + + it('should add colon after header key, if the value is empty string', function () { + request = new Request({ + 'method': 'GET', + 'header': [ + { + 'key': 'hello', + 'value': '' + } + ], + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('--header \'hello: \''); + }); + }); + + it('should return snippet with backslash(\\) as line continuation ' + + 'character for multiline code generation by default', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '' + } + }); + options = { + multiLine: true + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + snippetArray = snippet.split('\n'); + // Ignoring the last line as there is no line continuation character at last line + for (var i = 0; i < snippetArray.length - 1; i++) { + line = snippetArray[i]; + expect(line.charAt(line.length - 1)).to.equal('\\'); + } + }); + }); + + it('should return snippet with backtick(`) as line continuation ' + + 'character for multiline code generation', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '' + } + }); + options = { + multiLine: true, + lineContinuationCharacter: '`' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + snippetArray = snippet.split('\n'); + // Ignoring the last line as there is no line continuation character at last line + for (var i = 0; i < snippetArray.length - 1; i++) { + line = snippetArray[i]; + expect(line.charAt(line.length - 1)).to.equal('`'); + } + }); + }); + + it('should add content type if formdata field contains a content-type', function () { + request = new Request({ + 'method': 'POST', + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'json', + 'value': '{"hello": "world"}', + 'contentType': 'application/json', + 'type': 'text' + } + ] + }, + 'url': { + 'raw': 'http://postman-echo.com/post', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('--form'); + expect(snippet).to.contain('json='); + }); + }); + + it('should parse header with string value properly', function () { + request = new Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'foo', + 'value': '"bar"' + } + ], + 'body': { + 'mode': 'raw', + 'raw': '' + } + }); + options = { + longFormat: false + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.include("-H 'foo: \"bar\"'"); // eslint-disable-line quotes + }); + }); + + it('should return snippet without errors when request object has no body property', function () { + request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://google.com', + 'protocol': 'https', + 'host': [ + 'google', + 'com' + ] + } + }); + options = { + longFormat: false + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include("'https://google.com'"); // eslint-disable-line quotes + }); + }); + + it('should return snippet with JSON body in single line if multiline option is false', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '{\n "name": "John",\n "type": "names",\n "id": "123sdaw"\n}', + 'options': { + 'raw': { + 'language': 'json' + } + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + multiLine: false, + longFormat: false, + lineContinuationCharacter: '\\', + quoteType: 'single', + requestTimeoutInSeconds: 0, + followRedirect: true, + followOriginalHttpMethod: false + }; + + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.contain('-d \'{"name":"John","type":"names","id":"123sdaw"}\''); + }); + }); + + it('should return snippet with backslash(\\) character as line continuation ' + + 'character for multiline code generation', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '' + } + }); + options = { + multiLine: true, + lineContinuationCharacter: '\\' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + snippetArray = snippet.split('\n'); + // Ignoring the last line as there is no line continuation character at last line + for (var i = 0; i < snippetArray.length - 1; i++) { + line = snippetArray[i]; + expect(line.charAt(line.length - 1)).to.equal('\\'); + } + }); + }); + + it('should not encode queryParam unresolved variables and ' + + 'leave it inside double parenthesis {{xyz}}', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'url': { + 'raw': 'http://postman-echo.com/post?a={{xyz}}', + 'protocol': 'http', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ], + 'query': [ + { + 'key': 'a', + 'value': '{{xyz}}' + } + ] + } + }); + options = {}; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('http://postman-echo.com/post?a={{xyz}}'); + expect(snippet).to.not.include('http://postman-echo.com/post?a=%7B%7Bxyz%7D%7D'); + }); + }); + + it('should encode queryParams other than unresolved variables', function () { + request = new Request({ + 'method': 'POST', + 'header': [], + 'url': { + 'raw': 'http://postman-echo.com/post?a=b c', + 'protocol': 'http', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ], + 'query': [ + { + 'key': 'a', + 'value': 'b c' + } + ] + } + }); + options = {}; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('http://postman-echo.com/post?a=b%20c'); + expect(snippet).to.not.include('http://postman-echo.com/post?a=b c'); + }); + }); + + it('should trim header keys and not trim header values', function () { + var request = new Request({ + 'method': 'GET', + 'header': [ + { + 'key': ' key_containing_whitespaces ', + 'value': ' value_containing_whitespaces ' + } + ], + 'url': { + 'raw': 'https://google.com', + 'protocol': 'https', + 'host': [ + 'google', + 'com' + ] + } + }); + convert(request, { longFormat: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + // one extra space in matching the output because we add key:value in the snippet + expect(snippet).to.include( + `--header 'key_containing_whitespaces: value_containing_whitespaces '`); // eslint-disable-line quotes + }); + }); + + it('should generate snippets for no files in form data', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'no file', + 'value': '', + 'type': 'file', + 'src': [] + }, + { + 'key': 'no src', + 'value': '', + 'type': 'file' + }, + { + 'key': 'invalid src', + 'value': '', + 'type': 'file', + 'src': {} + } + ] + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--form \'no file=@/path/to/file\''); + expect(snippet).to.include('--form \'no src=@/path/to/file\''); + expect(snippet).to.include('--form \'invalid src=@/path/to/file\''); + }); + }); + + it('should generate correct --form flag for each field in multipart/form-data with text and file', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'textField', + 'value': '123', + 'type': 'text' + }, + { + 'key': 'fileField', + 'value': '', + 'type': 'file', + 'src': '/path/to/document.pdf' + } + ] + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + // Each field should have its own --form flag + expect(snippet).to.include('--form \'textField='); + expect(snippet).to.include('--form \'fileField=@/path/to/document.pdf\''); + // File path should NOT have inner double quotes + expect(snippet).to.not.include('@"/path/to/document.pdf"'); + }); + }); + + it('should generate valid snippets for single/double quotes in URL', function () { + // url = https://a"b'c.com/'d/"e + var request = new Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + // for single quote escaping inside single quotes involves changing of ' to '\'' + // expect => 'https://a"b'\''c.com/'\''d/"e' + expect(snippet).to.include("'https://a\"b'\\''c.com/'\\''d/\"e'"); // eslint-disable-line quotes + }); + }); + + it('should generate valid snippets when quoteType is "double"', function () { + // url = https://a"b'c.com/'d/"e + var request = new Request({ + 'method': 'POST', + 'body': { + 'mode': 'formdata', + 'formdata': [ + { + 'key': 'json', + 'value': '{"hello": "world"}', + 'contentType': 'application/json', + 'type': 'text' + } + ] + }, + 'url': { + 'raw': "https://a\"b'c.com/'d/\"e", // eslint-disable-line quotes + 'host': [ + 'a"b\'c', + 'com' + ] + } + }); + convert(request, {quoteType: 'double'}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).to.include('"a\\"b\'c.com"'); + expect(snippet).to.include('"json='); + }); + }); + + it('should add appropriate escaping characters when quote type is "double"', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '{"query":"test"}', + 'options': { + 'raw': { + 'language': 'json' + } + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + convert(request, { quoteType: 'double', longFormat: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).to.include('--body'); + expect(snippet).to.include('query'); + }); + }); + + it('should escape special characters when quoteType is "double"', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'raw', + 'raw': '{\r\n "hello": "$(whoami)"\r\n}', + 'options': { + 'raw': { + 'language': 'json' + } + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + // eslint-disable-next-line max-len + convert(request, { quoteType: 'double', lineContinuationCharacter: '^', longFormat: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + + expect(snippet).to.include('--body'); + expect(snippet).to.include('hello'); + }); + }); + + describe('getUrlStringfromUrlObject function', function () { + var rawUrl, urlObject, outputUrlString; + + it('should return empty string for an url object for an empty url or if no url object is passed', function () { + rawUrl = ''; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.be.empty; + outputUrlString = getUrlStringfromUrlObject(); + expect(outputUrlString).to.be.empty; + }); + + it('should add protocol if present in the url object', function () { + rawUrl = 'https://postman-echo.com'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal(rawUrl); + }); + + it('should add the auth information if present in the url object', function () { + rawUrl = 'https://user:password@postman-echo.com'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal(rawUrl); + }); + + it('should not add the auth information if user isn\'t present but' + + ' password is present in the url object', function () { + rawUrl = 'https://:password@postman-echo.com'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include(':password'); + }); + + it('should add host if present in the url object', function () { + rawUrl = 'https://postman-echo.com'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal(rawUrl); + }); + + it('should add port if present in the url object', function () { + rawUrl = 'https://postman-echo.com:8080'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal(rawUrl); + }); + + it('should add path if present in the url object', function () { + rawUrl = 'https://postman-echo.com/get'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal(rawUrl); + }); + + describe('queryParams', function () { + + it('should not encode unresolved query params', function () { + rawUrl = 'https://postman-echo.com/get?key={{value}}'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include('key=%7B%7Bvalue%7B%7B'); + expect(outputUrlString).to.equal(rawUrl); + }); + + it('should encode query params other than unresolved variables', function () { + rawUrl = 'https://postman-echo.com/get?key=\'a b c\''; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include('key=\'a b c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key=%27a%20b%20c%27'); + }); + + it('should not encode unresolved query params and ' + + 'encode every other query param, both present together', function () { + rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\''; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b+c%27'); + }); + + it('should not encode query params that are already encoded', function () { + rawUrl = 'https://postman-echo.com/get?query=urn%3Ali%3Afoo%3A62324'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal('https://postman-echo.com/get?query=urn%3Ali%3Afoo%3A62324'); + }); + + it('should discard disabled query params', function () { + urlObject = new Url({ + protocol: 'https', + host: 'postman-echo.com', + query: [ + { key: 'foo', value: 'bar' }, + { key: 'alpha', value: 'beta', disabled: true } + ] + }); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal('https://postman-echo.com?foo=bar'); + }); + }); + + it('should add hash if present in the url object', function () { + rawUrl = 'https://postmanm-echo.com/get#hash'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal(rawUrl); + }); + }); + + it('should always add HTTP method in POST request', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.match(/^postman request POST /); + }); + }); + + it('should add HTTP method in POST request even if body is not present', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.match(/^postman request POST /); + }); + }); + + it('should not add HTTP method in GET request', function () { + var request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + + convert(request, { followRedirect: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.match(/^postman request '/); + expect(snippet).to.not.match(/^postman request GET /); + }); + }); + + describe('followRedirect and followOriginalHttpMethod', function () { + it('should add --redirect-follow-method flag when followOriginalHttpMethod is true', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: true, followOriginalHttpMethod: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.match(/^postman request POST /); + expect(snippet).to.include('--redirects-follow-method'); + }); + }); + + it('should add --redirects-ignore flag when followRedirect is false', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'graphql', + 'graphql': { + 'query': '{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: "performers"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}', // eslint-disable-line + 'variables': '{\n\t"variable_key": "variable_value"\n}' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: false, followOriginalHttpMethod: false }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--redirects-ignore'); + expect(snippet).to.not.include('--redirects-follow-method'); + }); + }); + + it('should not add --redirects-ignore when followRedirect is true', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: true, followOriginalHttpMethod: false }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--redirects-ignore'); + }); + }); + + it('should add --max-redirects flag when maxRedirects is set', function () { + const request = new Request({ + 'method': 'POST', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + + convert(request, { followRedirect: true, maxRedirects: 5 }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--redirects-max 5'); + }); + }); + }); + + describe('should correctly handle NTLM auth', function () { + const sampleRequest = { + 'method': 'POST', + 'header': [], + 'auth': { + 'type': 'ntlm', + 'ntlm': [] + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }; + + it('when no username or password is present', function () { + const request = new Request(sampleRequest); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--auth-ntlm-username'); + expect(snippet).to.not.include('--auth-ntlm-password'); + }); + }); + + it('when empty username and password is present', function () { + const request = new Request(Object.assign({ auth: { + 'type': 'ntlm', + 'ntlm': [ + {key: 'username', value: ''}, + {key: 'password', value: ''} + ] + }}, sampleRequest)); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--auth-ntlm-username'); + expect(snippet).to.not.include('--auth-ntlm-password'); + }); + }); + + it('when correct username and password is present with single quotes as option', function () { + const request = new Request(_.set(sampleRequest, 'auth.ntlm', [ + {key: 'username', value: 'joh\'n'}, + {key: 'password', value: 'tennesse"e'} + ])); + + convert(request, { quoteType: 'single' }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--auth-ntlm-username \'joh\'\\\'\'n\''); + expect(snippet).to.include('--auth-ntlm-password \'tennesse"e\''); + }); + }); + + it('when correct username and password is present with double as option', function () { + const request = new Request(_.set(sampleRequest, 'auth.ntlm', [ + {key: 'username', value: 'joh\'n'}, + {key: 'password', value: 'tennesse"e'} + ])); + + convert(request, { quoteType: 'double' }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--auth-ntlm-username "joh\'n"'); + expect(snippet).to.include('--auth-ntlm-password "tennesse\\"e"'); + }); + }); + + it('when username and password is present with domain as well', function () { + const request = new Request(_.set(sampleRequest, 'auth.ntlm', [ + {key: 'username', value: 'joh\'n'}, + {key: 'password', value: 'tennesse"e'}, + {key: 'domain', value: 'radio'} + ])); + + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--auth-ntlm-username \'joh\'\\\'\'n\''); + expect(snippet).to.include('--auth-ntlm-password \'tennesse"e\''); + expect(snippet).to.include('--auth-ntlm-domain \'radio\''); + }); + }); + }); + + it('should use --body when request body type is file', function () { + var request = new Request({ + 'method': 'POST', + 'header': [], + 'body': { + 'mode': 'file', + 'file': { + 'src': 'file-path/collection123.json' + } + }, + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + + convert(request, { longFormat: true }, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--body \'@file-path/collection123.json\''); + }); + }); + + describe('indentType option', function () { + it('should use spaces for indentation when indentType is "Space"', function () { + request = new Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ], + 'body': { + 'mode': 'raw', + 'raw': '{"test": "data"}' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + multiLine: true, + indentType: 'Space', + indentCount: 2, + lineContinuationCharacter: '\\' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + // Check that lines are indented with spaces (the pattern should be: space + backslash + newline + spaces) + snippetArray = snippet.split('\n'); + // Second line onwards should have space indentation + for (var i = 1; i < snippetArray.length; i++) { + line = snippetArray[i]; + if (line.length > 0) { + // Line should start with spaces (indentCount = 2) + expect(line).to.match(/^ {2}/); + } + } + }); + }); + + it('should use tabs for indentation when indentType is "Tab"', function () { + request = new Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ], + 'body': { + 'mode': 'raw', + 'raw': '{"test": "data"}' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + multiLine: true, + indentType: 'Tab', + indentCount: 1, + lineContinuationCharacter: '\\' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + // Check that lines are indented with tabs + snippetArray = snippet.split('\n'); + // Second line onwards should have tab indentation + for (var i = 1; i < snippetArray.length; i++) { + line = snippetArray[i]; + if (line.length > 0) { + // Line should start with tab character + expect(line).to.match(/^\t/); + } + } + }); + }); + + it('should respect indentCount when using Space indentation', function () { + request = new Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'X-Custom-Header', + 'value': 'test' + } + ], + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + multiLine: true, + indentType: 'Space', + indentCount: 4, + lineContinuationCharacter: '\\' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + snippetArray = snippet.split('\n'); + // Second line onwards should have 4 spaces indentation + for (var i = 1; i < snippetArray.length; i++) { + line = snippetArray[i]; + if (line.length > 0) { + expect(line).to.match(/^ {4}/); + } + } + }); + }); + + it('should use Space indentation by default', function () { + request = new Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ], + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + multiLine: true, + lineContinuationCharacter: '\\' + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + snippetArray = snippet.split('\n'); + // Should use spaces by default (2 spaces based on default indentCount) + for (var i = 1; i < snippetArray.length; i++) { + line = snippetArray[i]; + if (line.length > 0) { + expect(line).to.match(/^ {2}/); + expect(line).to.not.match(/^\t/); + } + } + }); + }); + }); + + describe('debug option', function () { + it('should add --debug flag when debug option is true', function () { + request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + options = { + debug: true + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--debug'); + }); + }); + + it('should not add --debug flag when debug option is false', function () { + request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + options = { + debug: false + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--debug'); + }); + }); + + it('should not add --debug flag by default', function () { + request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + options = {}; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.not.include('--debug'); + }); + }); + + it('should add --debug flag with multiline format', function () { + request = new Request({ + 'method': 'POST', + 'header': [ + { + 'key': 'Content-Type', + 'value': 'application/json' + } + ], + 'body': { + 'mode': 'raw', + 'raw': '{"test": "data"}' + }, + 'url': { + 'raw': 'https://postman-echo.com/post', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'post' + ] + } + }); + options = { + debug: true, + multiLine: true, + longFormat: true + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--debug'); + // Verify it appears on its own line in multiline format + snippetArray = snippet.split('\n'); + var foundDebug = false; + for (var i = 0; i < snippetArray.length; i++) { + if (snippetArray[i].includes('--debug')) { + foundDebug = true; + break; + } + } + expect(foundDebug).to.be.true; + }); + }); + + it('should work with both quiet and debug flags together', function () { + request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/get', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }); + options = { + quiet: true, + debug: true, + longFormat: true + }; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('--quiet'); + expect(snippet).to.include('--debug'); + }); + }); + }); + }); +}); diff --git a/codegens/postman-cli/test/unit/fixtures/testcollection/collection.json b/codegens/postman-cli/test/unit/fixtures/testcollection/collection.json new file mode 100644 index 000000000..363346323 --- /dev/null +++ b/codegens/postman-cli/test/unit/fixtures/testcollection/collection.json @@ -0,0 +1,1453 @@ +{ + "info": { + "name": "Code-Gen Test Cases", + "_postman_id": "41182fad-912e-6bc9-d6b9-dfb6f5bf5ffb", + "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Request Headers with disabled headers", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "try {", + " tests[\"Body contains headers\"] = responseBody.has(\"headers\");", + " responseJSON = JSON.parse(responseBody);", + " tests[\"Header contains host\"] = \"host\" in responseJSON.headers;", + " tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;", + "}", + "catch (e) { }", + "", + "", + "", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "not-disabled-header", + "value": "ENABLED" + }, + { + "key": "disabled header", + "value": "DISABLED", + "disabled": true + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "GET Request with disabled query", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "tests['response json contains headers'] = _.has(responseJSON, 'headers');", + "tests['response json contains args'] = _.has(responseJSON, 'args');", + "tests['response json contains url'] = _.has(responseJSON, 'url');", + "", + "tests['args key contains argument passed as url parameter'] = ('test' in responseJSON.args);", + "tests['args passed via request url params has value \"123\"'] = (_.get(responseJSON, 'args.test') === \"123\");" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/get?test=123&anotherone=232", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "test", + "value": "123", + "equals": true + }, + { + "key": "anotherone", + "value": "232", + "equals": true + }, + { + "key": "anotheroneone", + "value": "sdfsdf", + "equals": true, + "disabled": true + } + ] + }, + "description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested." + }, + "response": [] + }, + { + "name": "POST Raw Text", + "event": [ + { + "listen": "test", + "script": { + "id": "753f8a33-adb6-402f-8d19-386c1981ecb6", + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "\"'Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, \"id\" \"se\\\"mper\" sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.'\"" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST form data with file", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded", + "disabled": true + }, + { + "key": "content-type", + "value": "application/json", + "disabled": true + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "fdjks", + "value": "dsf", + "type": "text" + }, + { + "key": "&^%", + "value": "helo", + "type": "text" + }, + { + "key": "12", + "value": "\"23\"", + "description": "", + "type": "text" + }, + { + "key": "'123'", + "value": "'\"23\\\"4\\\"\"'", + "description": "", + "type": "text" + }, + { + "key": "", + "value": "", + "description": "", + "type": "text", + "disabled": true + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST urlencoded data with disabled entries", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "1", + "value": "a", + "description": "", + "type": "text" + }, + { + "key": "2", + "value": "b", + "description": "", + "type": "text" + }, + { + "key": "\"\"12\"\"", + "value": "\"23\"", + "description": "", + "type": "text" + }, + { + "key": "'1\"2\\\"\"3'", + "value": "'1\"23\"4'", + "description": "", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post/?hardik=\"me\"", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post", + "" + ], + "query": [ + { + "key": "hardik", + "value": "\"me\"", + "equals": true + } + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST json with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [ + { + "id": "db02f994-5ac4-41e1-835a-f49a14acbb6e", + "name": "POST json with raw", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "Lets a server whitelist headers that browsers are allowed to access." + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Length", + "value": "385", + "name": "Content-Length", + "description": "The length of the response body in octets (8-bit bytes)" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Wed, 07 Feb 2018 10:06:15 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "ETag", + "value": "W/\"215-u7EU1nFtauIn0/aVifjuXA\"", + "name": "ETag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Vary", + "value": "X-HTTP-Method-Override, Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + }, + { + "key": "set-cookie", + "value": "sails.sid=s%3AxRBxgrc9M-jKK_l1mX3y3rM_ry8wYLz4.Of4qpOzd9hi6uO0sAQIj%2Bxs2VeppWxYjJa4OpIW3PKg; Path=/; HttpOnly", + "name": "set-cookie", + "description": "an HTTP cookie" + } + ], + "cookie": [ + { + "expires": "Tue Jan 19 2038 08:44:07 GMT+0530 (IST)", + "httpOnly": true, + "domain": "postman-echo.com", + "path": "/", + "secure": false, + "value": "s%3AxRBxgrc9M-jKK_l1mX3y3rM_ry8wYLz4.Of4qpOzd9hi6uO0sAQIj%2Bxs2VeppWxYjJa4OpIW3PKg", + "key": "sails.sid" + } + ], + "body": "{\"args\":{},\"data\":\"{\\n \\\"json\\\": \\\"Test-Test\\\"\\n}\",\"files\":{},\"form\":{},\"headers\":{\"host\":\"postman-echo.com\",\"content-length\":\"25\",\"accept\":\"*/*\",\"accept-encoding\":\"gzip, deflate\",\"cache-control\":\"no-cache\",\"content-type\":\"text/plain\",\"cookie\":\"sails.sid=s%3AkOgtF1XmXtVFx-Eg3S7-37BKKaMqMDPe.hnwldNwyvsaASUiRR0Y0vcowadkMXO4HMegTeVIPgqo\",\"postman-token\":\"2ced782f-a141-428e-8af6-04ce954a77d5\",\"user-agent\":\"PostmanRuntime/7.1.1\",\"x-forwarded-port\":\"443\",\"x-forwarded-proto\":\"https\"},\"json\":null,\"url\":\"https://postman-echo.com/post\"}" + } + ] + }, + { + "name": "POST javascript with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/javascript" + } + ], + "body": { + "mode": "raw", + "raw": "var val = 6;\nconsole.log(val);" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/xml with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/xml" + } + ], + "body": { + "mode": "raw", + "raw": "\n Test Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/html with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/html" + } + ], + "body": { + "mode": "raw", + "raw": "\n Test Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Resolve URL", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/:action", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + ":action" + ], + "variable": [ + { + "key": "action", + "value": "post" + } + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PUT Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + }, + "description": "The HTTP `PUT` request method is similar to HTTP `POST`. It too is meant to \ntransfer data to a server (and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `PUT` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following \nraw HTTP request,\n\n> PUT /hi/there?hand=wave\n>\n> \n\n\n" + }, + "response": [] + }, + { + "name": "PATCH Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." + }, + "url": { + "raw": "https://postman-echo.com/patch", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "patch" + ] + }, + "description": "The HTTP `PATCH` method is used to update resources on a server. The exact\nuse of `PATCH` requests depends on the server in question. There are a number\nof server implementations which handle `PATCH` differently. Technically, \n`PATCH` supports both Query String parameters and a Request Body.\n\nThis endpoint accepts an HTTP `PATCH` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "DELETE Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + }, + { + "key": "Content-Length", + "value": "1000", + "disabled": true + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "dsfs", + "value": "sfdds", + "description": "", + "type": "text" + }, + { + "key": "sfdsdf", + "value": "sdf", + "description": "", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/delete", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "delete" + ] + }, + "description": "The HTTP `DELETE` method is used to delete resources on a server. The exact\nuse of `DELETE` requests depends on the server implementation. In general, \n`DELETE` requests support both, Query String parameters as well as a Request \nBody.\n\nThis endpoint accepts an HTTP `DELETE` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "OPTIONS to postman echo", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "OPTIONS", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "HEAD request", + "request": { + "method": "HEAD", + "header": [ + { + "key": "hello", + "value": "helloagain", + "disabled": true + } + ], + "body": {}, + "url": { + "raw": "https://www.postman-echo.com/head", + "protocol": "https", + "host": [ + "www", + "postman-echo", + "com" + ], + "path": [ + "head" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "LINK request", + "request": { + "method": "LINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "UNLINK request", + "request": { + "method": "UNLINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "LOCK request", + "request": { + "method": "LOCK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "UNLOCK request", + "request": { + "method": "UNLOCK", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "PROPFIND request", + "request": { + "method": "PROPFIND", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "VIEW request", + "request": { + "method": "VIEW", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "PURGE Request", + "request": { + "method": "PURGE", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + }, + "description": null + }, + "response": [ + { + "id": "c0d81a4f-46ee-4ce4-a602-37b7d55b9983", + "name": "PURGE Request", + "originalRequest": { + "method": "PURGE", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + } + }, + "status": "Not Found", + "code": 404, + "_postman_previewlanguage": "text", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "" + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "" + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*", + "name": "Access-Control-Allow-Origin", + "description": "" + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "" + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "" + }, + { + "key": "Content-Length", + "value": "152", + "name": "Content-Length", + "description": "" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "" + }, + { + "key": "Date", + "value": "Tue, 13 Feb 2018 13:58:56 GMT", + "name": "Date", + "description": "" + }, + { + "key": "ETag", + "value": "W/\"a7-kIxN5L9H0YwilUQPUUio9A\"", + "name": "ETag", + "description": "" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "" + }, + { + "key": "Vary", + "value": "Accept-Encoding", + "name": "Vary", + "description": "" + } + ], + "cookie": [], + "responseTime": "375", + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" + } + ] + }, + { + "name": "COPY Request", + "request": { + "method": "COPY", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [ + { + "id": "6367d22f-0cf7-48f2-a41d-5b9ed11cbff5", + "name": "COPY Request", + "originalRequest": { + "method": "COPY", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "true", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "host,connection,accept-encoding,x-forwarded-for,cf-ray,x-forwarded-proto,cf-visitor,cache-control,postman-token,user-agent,accept,cookie,cf-connecting-ip,x-request-id,x-forwarded-port,via,connect-time,x-request-start,total-route-time,content-length", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "COPY", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "CF-RAY", + "value": "3fb595d5facaa302-HKG", + "name": "CF-RAY", + "description": "Custom header" + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Wed, 14 Mar 2018 09:06:37 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "Etag", + "value": "W/\"4ac-YP9NIoQ5TiGJRPuQSZMKtA\"", + "name": "Etag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Expect-CT", + "value": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", + "name": "Expect-CT", + "description": "Custom header" + }, + { + "key": "Server", + "value": "cloudflare", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Transfer-Encoding", + "value": "chunked", + "name": "Transfer-Encoding", + "description": "The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, gzip, identity." + }, + { + "key": "Vary", + "value": "Accept, Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + }, + { + "key": "Via", + "value": "1.1 vegur", + "name": "Via", + "description": "Informs the client of proxies through which the response was sent." + }, + { + "key": "X-Powered-By", + "value": "mockbin", + "name": "X-Powered-By", + "description": "Specifies the technology (ASP.NET, PHP, JBoss, e.g.) supporting the web application (version details are often in X-Runtime, X-Version, or X-AspNet-Version)" + } + ], + "cookie": [ + { + "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", + "httpOnly": true, + "domain": "postman-echo.com", + "path": "/", + "secure": false, + "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", + "key": "__cfduid" + } + ], + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + } + ] + } + ] +} \ No newline at end of file diff --git a/codegens/postman-cli/test/unit/validation.test.js b/codegens/postman-cli/test/unit/validation.test.js new file mode 100644 index 000000000..23050f424 --- /dev/null +++ b/codegens/postman-cli/test/unit/validation.test.js @@ -0,0 +1,30 @@ +var expect = require('chai').expect, + path = require('path'), + + package = require(path.resolve('.', 'package.json')); + + +describe('package.json', function () { + it('should have com_postman_plugin object with valid properties', function () { + expect(package).to.have.property('com_postman_plugin'); + + expect(package.com_postman_plugin.type).to.equal('code_generator'); + expect(package.com_postman_plugin.lang).to.be.a('string'); + expect(package.com_postman_plugin.variant).to.be.a('string'); + expect(package.com_postman_plugin.syntax_mode).to.be.equal('powershell'); + }); + it('should have main property with relative path to object with convert property', function () { + var languageModule; + + expect(package.main).to.be.a('string'); + + try { + languageModule = require(path.resolve('.', package.main)); + } + catch (error) { + console.error(error); + } + expect(languageModule).to.be.a('object'); + expect(languageModule.convert).to.be.a('function'); + }); +}); diff --git a/codegens/powershell-restmethod/.gitignore b/codegens/powershell-restmethod/.gitignore index cbf9ecbc5..ff20256ea 100644 --- a/codegens/powershell-restmethod/.gitignore +++ b/codegens/powershell-restmethod/.gitignore @@ -11,6 +11,12 @@ pids *.seed *.pid.lock +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Prevent IDE stuff .idea .vscode diff --git a/codegens/powershell-restmethod/lib/index.js b/codegens/powershell-restmethod/lib/index.js index 7fb734b68..6d253ac08 100644 --- a/codegens/powershell-restmethod/lib/index.js +++ b/codegens/powershell-restmethod/lib/index.js @@ -1,5 +1,6 @@ var _ = require('./lodash'), sanitize = require('./util').sanitize, + sanitizeSingleQuotes = require('./util').sanitizeSingleQuotes, sanitizeOptions = require('./util').sanitizeOptions, addFormParam = require('./util').addFormParam, path = require('path'); @@ -82,7 +83,7 @@ function parseFormData (body, trim) { * @param {boolean} trim trim body option */ function parseRawBody (body, trim) { - return `$body = "${sanitize(body.toString(), trim)}"\n`; + return `$body = @"\n${sanitize(body.toString(), trim, false)}\n"@\n`; } /** @@ -289,12 +290,12 @@ function convert (request, options, callback) { } if (_.includes(VALID_METHODS, request.method)) { - codeSnippet += `$response = Invoke-RestMethod '${request.url.toString().replace(/'/g, '\'\'')}' -Method '` + + codeSnippet += `$response = Invoke-RestMethod '${sanitizeSingleQuotes(request.url.toString())}' -Method '` + `${request.method}' -Headers $headers`; } else { - codeSnippet += `$response = Invoke-RestMethod '${request.url.toString()}' -CustomMethod ` + - `'${request.method}' -Headers $headers`; + codeSnippet += `$response = Invoke-RestMethod '${sanitizeSingleQuotes(request.url.toString())}' -CustomMethod ` + + `'${sanitizeSingleQuotes(request.method)}' -Headers $headers`; } if (bodySnippet !== '') { codeSnippet += ' -Body $body'; diff --git a/codegens/powershell-restmethod/lib/util.js b/codegens/powershell-restmethod/lib/util.js index 9d313c756..af3ce9d45 100644 --- a/codegens/powershell-restmethod/lib/util.js +++ b/codegens/powershell-restmethod/lib/util.js @@ -5,9 +5,10 @@ * * @param {String} inputString * @param {Boolean} [trim] - indicates whether to trim string or not + * @param {Boolean} shouldEscapeNewLine - indicates whether to escape newline * @returns {String} */ -function sanitize (inputString, trim) { +function sanitize (inputString, trim, shouldEscapeNewLine = true) { if (typeof inputString !== 'string') { return ''; } @@ -15,11 +16,30 @@ function sanitize (inputString, trim) { .replace(/`/g, '``') .replace(/\$/g, '`$') .replace(/\\/g, '\`\\') - .replace(/\"/g, '\`\"') - .replace(/\n/g, '\`n'); + .replace(/\"/g, '\`\"'); + + if (shouldEscapeNewLine) { + inputString = inputString.replace(/\n/g, '\`n'); + } return trim ? inputString.trim() : inputString; } +/** + * + * @param {String} inputString - input string + * @returns {String} - sanitized string + */ +function sanitizeSingleQuotes (inputString) { + if (typeof inputString !== 'string') { + return ''; + } + inputString = inputString + .replace(/'/g, '\'\''); + + return inputString; + +} + /** * sanitizes input options * @@ -122,6 +142,7 @@ function addFormParam (array, key, type, val, disabled, contentType) { module.exports = { sanitize: sanitize, + sanitizeSingleQuotes: sanitizeSingleQuotes, sanitizeOptions: sanitizeOptions, addFormParam: addFormParam }; diff --git a/codegens/powershell-restmethod/npm-shrinkwrap.json b/codegens/powershell-restmethod/npm-shrinkwrap.json index eac67ebdb..60525bf88 100644 --- a/codegens/powershell-restmethod/npm-shrinkwrap.json +++ b/codegens/powershell-restmethod/npm-shrinkwrap.json @@ -16,7 +16,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, "chalk": { @@ -46,9 +46,9 @@ "dev": true }, "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -57,7 +57,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true }, "escape-string-regexp": { @@ -67,13 +67,13 @@ "dev": true }, "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "1.0.6", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, @@ -84,18 +84,18 @@ "dev": true }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "requires": { - "mime-db": "1.40.0" + "mime-db": "1.52.0" } }, "nanoid": { diff --git a/codegens/powershell-restmethod/test/ci-install.sh b/codegens/powershell-restmethod/test/ci-install.sh new file mode 100755 index 000000000..cb84aa3bd --- /dev/null +++ b/codegens/powershell-restmethod/test/ci-install.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing Powershell" +sudo apt-get install powershell -y diff --git a/codegens/powershell-restmethod/test/newman/newman.test.js b/codegens/powershell-restmethod/test/newman/newman.test.js index 77bf3a603..94825476c 100644 --- a/codegens/powershell-restmethod/test/newman/newman.test.js +++ b/codegens/powershell-restmethod/test/newman/newman.test.js @@ -16,7 +16,7 @@ describe('Powershell Restmethod Converter', function () { // Poweshell does not support headers with duplicate keys // If a Post request has no body then powershell will still send // an empty string as body, which is not what is expected in newman tests - skipCollections: ['sameNameHeadersCollection', 'emptyFormdataCollection'] + skipCollections: ['sameNameHeadersCollection', 'emptyFormdataCollection', 'unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); }); diff --git a/codegens/powershell-restmethod/test/unit/convert.test.js b/codegens/powershell-restmethod/test/unit/convert.test.js index 1f0f1c008..0ec84f9af 100644 --- a/codegens/powershell-restmethod/test/unit/convert.test.js +++ b/codegens/powershell-restmethod/test/unit/convert.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), exec = require('shelljs').exec, newman = require('newman'), parallel = require('async').parallel, @@ -116,7 +116,7 @@ describe('Powershell-restmethod converter', function () { mainCollection.item.forEach(function (item) { // Skipping tests for Travis CI, till powershell dependency issue is sorted on travis it.skip(item.name, function (done) { - var request = new sdk.Request(item.request), + var request = new Request(item.request), collection = { item: [ { @@ -156,11 +156,11 @@ describe('Powershell-restmethod converter', function () { 'raw': 'Hello world' }, 'url': { - 'raw': 'https://mockbin.org/request', + 'raw': 'https://postman-echo.com/request', 'protocol': 'https', 'host': [ - 'mockbin', - 'org' + 'postman-echo', + 'com' ], 'path': [ 'request' @@ -168,7 +168,7 @@ describe('Powershell-restmethod converter', function () { }, 'description': 'Description' }, - pmRequest = new sdk.Request(request), + pmRequest = new Request(request), options = { requestTimeout: 10000, multiLine: true, @@ -180,13 +180,16 @@ describe('Powershell-restmethod converter', function () { expect.fail(null, null, error); return; } + const lines = snippet.split('\n'); expect(lines[0]).to .eql('$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"'); expect(lines[1]).to.eql('$headers.Add("Content-Type", "text/plain")'); - expect(lines[3]).to.eql('$body = "Hello world"'); - expect(lines[5]).to.eql('$response = Invoke-RestMethod \'https://mockbin.org/request\' -Method \'POST\' -Headers $headers -Body $body -TimeoutSec 10'); // eslint-disable-line max-len - expect(lines[6]).to.eql('$response | ConvertTo-Json'); + expect(lines[3]).to.eql('$body = @"'); + expect(lines[4]).to.eql('Hello world'); + expect(lines[5]).to.eql('"@'); + expect(lines[7]).to.eql('$response = Invoke-RestMethod \'https://postman-echo.com/request\' -Method \'POST\' -Headers $headers -Body $body -TimeoutSec 10'); // eslint-disable-line max-len + expect(lines[8]).to.eql('$response | ConvertTo-Json'); }); }); }); @@ -195,7 +198,7 @@ describe('Powershell-restmethod converter', function () { var request, options; it('should add a TimeoutSec argument when timeout is set to non zero value', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 1000 }; @@ -210,7 +213,7 @@ describe('Powershell-restmethod converter', function () { }); it('should not add a TimeoutSec argument when timeout is set to 0', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { requestTimeout: 0 }; @@ -225,7 +228,7 @@ describe('Powershell-restmethod converter', function () { }); it('should add a MaximumRedirection set to 0 argument when followRedirect is not allowed', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { followRedirect: false }; @@ -240,7 +243,7 @@ describe('Powershell-restmethod converter', function () { }); it('should not add a MaximumRedirection argument when followRedirect is allowed', function () { - request = new sdk.Request(mainCollection.item[0].request); + request = new Request(mainCollection.item[0].request); options = { followRedirect: true }; @@ -255,7 +258,7 @@ describe('Powershell-restmethod converter', function () { }); it('should default to mode raw when body mode is some random value', function () { - request = new sdk.Request(mainCollection.item[2].request); + request = new Request(mainCollection.item[2].request); request.body.mode = 'random'; request.body[request.body.mode] = {}; options = {}; @@ -269,7 +272,7 @@ describe('Powershell-restmethod converter', function () { }); it('should generate snippet for file body mode', function () { - request = new sdk.Request({ + request = new Request({ 'url': 'https://echo.getpostman.com/post', 'method': 'POST', 'body': { @@ -294,7 +297,7 @@ describe('Powershell-restmethod converter', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -321,7 +324,7 @@ describe('Powershell-restmethod converter', function () { }); it('should include graphql body in the snippet', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -354,7 +357,7 @@ describe('Powershell-restmethod converter', function () { }); it('should generate snippets(not error out) for requests with multiple/no file in formdata', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -423,7 +426,7 @@ describe('Powershell-restmethod converter', function () { }); it('should add content type if formdata field contains a content-type', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -459,7 +462,7 @@ describe('Powershell-restmethod converter', function () { }); it('should generate valid snippet for single/double quotes in url', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -495,8 +498,47 @@ describe('Powershell-restmethod converter', function () { }); }); + it('should generate valid snippet when single quotes in custom request method', function () { + var request = new Request({ + // eslint-disable-next-line quotes + 'method': "TEST';DIR;#'", + 'header': [], + 'url': { + 'raw': 'https://postman-echo.com/get?query1=b\'b&query2=c"c', + 'protocol': 'https', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ], + 'query': [ + { + 'key': 'query1', + 'value': "b'b" // eslint-disable-line quotes + }, + { + 'key': 'query2', + 'value': 'c"c' + } + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + // An extra single quote is placed before a single quote to escape a single quote inside a single quoted string + // eslint-disable-next-line quotes + expect(snippet).to.include("-CustomMethod 'TEST'';DIR;#'''"); + }); + }); + + it('should generate snippet for form data params with no type key present', function () { - var request = new sdk.Request({ + var request = new Request({ method: 'POST', header: [], url: { diff --git a/codegens/powershell-restmethod/test/unit/fixtures/testcollection/collection.json b/codegens/powershell-restmethod/test/unit/fixtures/testcollection/collection.json index 264965912..5afead2ea 100644 --- a/codegens/powershell-restmethod/test/unit/fixtures/testcollection/collection.json +++ b/codegens/powershell-restmethod/test/unit/fixtures/testcollection/collection.json @@ -49,11 +49,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -123,11 +123,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request?test=123&anotherone=232", + "raw": "https://postman-echo.com/request?test=123&anotherone=232", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -197,11 +197,11 @@ "raw": "\"'Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, \"id\" \"se\\\"mper\" sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.'\"" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -368,11 +368,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -430,11 +430,11 @@ "raw": "{\n \"json\": \"Test-Test\"\n}" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -615,11 +615,11 @@ "raw": "var val = 6;\nconsole.log(val);" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -670,11 +670,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -725,11 +725,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -754,14 +754,13 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request/:action", + "raw": "https://postman-echo.com/:action", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ - "request", ":action" ], "variable": [ @@ -814,11 +813,11 @@ "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -867,11 +866,11 @@ "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -945,11 +944,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1024,11 +1023,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1053,11 +1052,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1082,11 +1081,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1111,11 +1110,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1132,11 +1131,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1161,11 +1160,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1190,13 +1189,11 @@ "raw": "" }, "url": { - "raw": "https://16c2790a-a0a2-4a4b-88c6-46f6459e715f.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "16c2790a-a0a2-4a4b-88c6-46f6459e715f", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1210,11 +1207,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1231,11 +1228,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1252,11 +1249,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1368,14 +1365,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/python-http.client/.gitignore b/codegens/python-http.client/.gitignore index ae3c28f23..bd2ffa72b 100644 --- a/codegens/python-http.client/.gitignore +++ b/codegens/python-http.client/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/python-http.client/lib/python-httpclient.js b/codegens/python-http.client/lib/python-httpclient.js index f0d80f621..f33886b85 100644 --- a/codegens/python-http.client/lib/python-httpclient.js +++ b/codegens/python-http.client/lib/python-httpclient.js @@ -1,5 +1,5 @@ var _ = require('./lodash'), - sdk = require('postman-collection'), + { Url } = require('postman-collection/lib/collection/url'), sanitize = require('./util/sanitize').sanitize, sanitizeOptions = require('./util/sanitize').sanitizeOptions, addFormParam = require('./util/sanitize').addFormParam, @@ -113,7 +113,7 @@ self = module.exports = { identity = options.indentType === 'Tab' ? '\t' : ' '; indentation = identity.repeat(options.indentCount); - url = sdk.Url.parse(request.url.toString()); + url = Url.parse(request.url.toString()); host = url.host ? url.host.join('.') : ''; path = url.path ? '/' + url.path.join('/') : '/'; query = url.query ? _.reduce(url.query, (accum, q) => { @@ -140,7 +140,12 @@ self = module.exports = { snippet += 'from codecs import encode\n'; } snippet += '\n'; - snippet += `conn = http.client.HTTPSConnection("${sanitize(host)}"`; + if (request.url.protocol === 'http') { + snippet += `conn = http.client.HTTPConnection("${sanitize(host)}"`; + } + else { + snippet += `conn = http.client.HTTPSConnection("${sanitize(host)}"`; + } snippet += url.port ? `, ${request.url.port}` : ''; snippet += options.requestTimeout !== 0 ? `, timeout = ${options.requestTimeout})\n` : ')\n'; diff --git a/codegens/python-http.client/package-lock.json b/codegens/python-http.client/package-lock.json deleted file mode 100644 index 58f4120e4..000000000 --- a/codegens/python-http.client/package-lock.json +++ /dev/null @@ -1,360 +0,0 @@ -{ - "name": "@postman/codegen-python-http.client", - "version": "0.1.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "charset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/charset/-/charset-1.0.1.tgz", - "integrity": "sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==" - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz", - "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==" - }, - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" - } - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "faker": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/faker/-/faker-5.1.0.tgz", - "integrity": "sha512-RrWKFSSA/aNLP0g3o2WW1Zez7/MnMr7xkiZmoCfAGZmdkDQZ6l2KtuXHN5XjdvpRjDl8+3vf+Rrtl06Z352+Mw==" - }, - "file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, - "http-reasons": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", - "integrity": "sha1-qVPKZwB4Zp3eFCzomUAbnW6F07Q=" - }, - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "liquid-json": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/liquid-json/-/liquid-json-0.3.1.tgz", - "integrity": "sha1-kVWhgTbYprJhXl8W+aJEira1Duo=" - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" - }, - "marked": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.2.0.tgz", - "integrity": "sha512-tiRxakgbNPBr301ihe/785NntvYyhxlqcL3YaC8CaxJQh7kiaEtrN9B/eK2I2943Yjkh5gw25chYFDQhOMCwMA==" - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" - }, - "mime-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.0.tgz", - "integrity": "sha1-4p+IkeKE14JwJG8AUNaDS9u+EzI=", - "requires": { - "charset": "^1.0.0" - } - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "requires": { - "mime-db": "1.44.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "postcss": { - "version": "7.0.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.35.tgz", - "integrity": "sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "postman-collection": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-3.6.8.tgz", - "integrity": "sha512-TNPaK2dpVRhttUFo/WN0ReopXEtuSQMktwcvwJbQ0z8K+5hftvyx2ia40xgg9qFl/Ra78qoNTUmLL1s3lRqLMg==", - "requires": { - "escape-html": "1.0.3", - "faker": "5.1.0", - "file-type": "3.9.0", - "http-reasons": "0.1.0", - "iconv-lite": "0.6.2", - "liquid-json": "0.3.1", - "lodash": "4.17.20", - "marked": "1.2.0", - "mime-format": "2.0.0", - "mime-types": "2.1.27", - "postman-url-encoder": "3.0.0", - "sanitize-html": "1.20.1", - "semver": "7.3.2", - "uuid": "3.4.0" - } - }, - "postman-url-encoder": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.0.tgz", - "integrity": "sha512-bk5wus5/5Ei9pbh+sQXaAxS5n4ZwiNAaIA8VBvRcXP6QyKcue2yF6xk1HqdtaZoH1G8+6509SVeOBojoFQ7nrA==", - "requires": { - "punycode": "^2.1.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sanitize-html": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", - "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", - "requires": { - "chalk": "^2.4.1", - "htmlparser2": "^3.10.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.mergewith": "^4.6.1", - "postcss": "^7.0.5", - "srcset": "^1.0.0", - "xtend": "^4.0.1" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - } - } -} diff --git a/codegens/python-http.client/package.json b/codegens/python-http.client/package.json index 2270522b1..6ec8c3661 100644 --- a/codegens/python-http.client/package.json +++ b/codegens/python-http.client/package.json @@ -27,7 +27,7 @@ "license": "Apache-2.0", "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/python-http.client", "dependencies": { - "postman-collection": "3.6.8" + "postman-collection": "5.0.0" }, "devDependencies": {}, "engines": { diff --git a/codegens/python-http.client/test/newman/newman.test.js b/codegens/python-http.client/test/newman/newman.test.js index f2f8132b8..922502fd0 100644 --- a/codegens/python-http.client/test/newman/newman.test.js +++ b/codegens/python-http.client/test/newman/newman.test.js @@ -13,7 +13,8 @@ describe('Convert for different types of request', function () { testConfig = { fileName: 'codesnippet.py', runScript: 'PYTHONIOENCODING=utf-8 python3 codesnippet.py', - skipCollections: ['redirectCollection', 'sameNameHeadersCollection'] + skipCollections: ['redirectCollection', 'sameNameHeadersCollection', 'unsupportedMethods', + 'queryParamsCollection'] }; runNewmanTest(convert, options, testConfig); }); diff --git a/codegens/python-http.client/test/unit/converter.test.js b/codegens/python-http.client/test/unit/converter.test.js index 8d37b9282..b409c31e6 100644 --- a/codegens/python-http.client/test/unit/converter.test.js +++ b/codegens/python-http.client/test/unit/converter.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), convert = require('../../lib/index').convert, getOptions = require('../../lib/index').getOptions, parseBody = require('../../lib/util/parseBody'), @@ -9,7 +9,7 @@ var expect = require('chai').expect, describe('Python-http.client converter', function () { describe('convert function', function () { - var request = new sdk.Request(mainCollection.item[0].request), + var request = new Request(mainCollection.item[0].request), snippetArray; const SINGLE_SPACE = ' '; @@ -37,7 +37,7 @@ describe('Python-http.client converter', function () { it('should parse the url correctly even if the host and path are wrong in the url object', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'body': { 'mode': 'raw', @@ -65,7 +65,7 @@ describe('Python-http.client converter', function () { }); it('should add content type if formdata field contains a content-type', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -100,7 +100,7 @@ describe('Python-http.client converter', function () { }); it('should convert JSON tokens into appropriate python tokens', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -154,7 +154,7 @@ describe('Python-http.client converter', function () { }); it('should generate snippet with requestTimeout option', function () { - var request = new sdk.Request(mainCollection.item[0].request); + var request = new Request(mainCollection.item[0].request); convert(request, { requestTimeout: 2000 }, function (error, snippet) { if (error) { expect.fail(null, null, error); @@ -165,7 +165,7 @@ describe('Python-http.client converter', function () { }); it('should generate snippet when url is not provied', function () { - var request = new sdk.Request({ + var request = new Request({ 'name': 'test', 'request': { 'method': 'GET', @@ -186,7 +186,7 @@ describe('Python-http.client converter', function () { }); it('should generate snippet with correct indent when body mode is formdata', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -220,7 +220,7 @@ describe('Python-http.client converter', function () { }); it('should add port in the options when host has port specified', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -246,7 +246,7 @@ describe('Python-http.client converter', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -273,7 +273,7 @@ describe('Python-http.client converter', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -324,7 +324,7 @@ describe('Python-http.client converter', function () { it('should generate valid snippets for single/double quotes in URL', function () { // url = https://a"b'c.com/'d/"e - var request = new sdk.Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes + var request = new Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes convert(request, {}, function (error, snippet) { if (error) { expect.fail(null, null, error); @@ -336,10 +336,33 @@ describe('Python-http.client converter', function () { }); }); + it('should generate valid snippets when url uses http protocol', function () { + var request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'http://localhost:3000', + 'protocol': 'http', + 'host': [ + 'localhost' + ], + 'port': '3000' + }, + 'response': [] + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('conn = http.client.HTTPConnection("localhost", 3000)'); + }); + }); + }); describe('parseBody function', function () { - var requestEmptyFormdata = new sdk.Request({ + var requestEmptyFormdata = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -347,7 +370,7 @@ describe('Python-http.client converter', function () { 'formdata': [] } }), - requestEmptyUrlencoded = new sdk.Request({ + requestEmptyUrlencoded = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -355,7 +378,7 @@ describe('Python-http.client converter', function () { 'urlencoded': [] } }), - requestEmptyRaw = new sdk.Request({ + requestEmptyRaw = new Request({ 'method': 'POST', 'header': [], 'body': { diff --git a/codegens/python-http.client/test/unit/fixtures/sample_collection.json b/codegens/python-http.client/test/unit/fixtures/sample_collection.json index 72025ab3d..234fd60a7 100644 --- a/codegens/python-http.client/test/unit/fixtures/sample_collection.json +++ b/codegens/python-http.client/test/unit/fixtures/sample_collection.json @@ -1428,11 +1428,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1449,13 +1449,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1469,13 +1467,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1489,13 +1485,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, diff --git a/codegens/python-requests/.gitignore b/codegens/python-requests/.gitignore index 9fcc07d50..4def0bfe8 100644 --- a/codegens/python-requests/.gitignore +++ b/codegens/python-requests/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/python-requests/lib/util/parseBody.js b/codegens/python-requests/lib/util/parseBody.js index 8a11898be..f7cce739e 100644 --- a/codegens/python-requests/lib/util/parseBody.js +++ b/codegens/python-requests/lib/util/parseBody.js @@ -31,7 +31,7 @@ var _ = require('../lodash'), 'js': 'text/javascript', 'json': 'application/json', 'jsonld': 'application/ld+json', - 'mid': 'audip/midi', + 'mid': 'audio/midi', 'midi': 'audio/midi', 'mjs': 'text/javascript', 'mp3': 'audio/mpeg', @@ -156,7 +156,7 @@ module.exports = function (request, indentation, bodyTrim, contentType) { catch (e) { graphqlVariables = {}; } - requestBody += `payload=${sanitize(JSON.stringify({ + requestBody += `payload = ${sanitize(JSON.stringify({ query: query, variables: graphqlVariables }), @@ -169,10 +169,10 @@ module.exports = function (request, indentation, bodyTrim, contentType) { return `${sanitize(value.key, request.body.mode, bodyTrim)}=` + `${sanitize(value.value, request.body.mode, bodyTrim)}`; }); - requestBody += `payload='${bodyDataMap.join('&')}'\n`; + requestBody += `payload = '${bodyDataMap.join('&')}'\n`; } else { - requestBody = 'payload={}\n'; + requestBody = 'payload = {}\n'; } return requestBody; case 'formdata': @@ -194,19 +194,19 @@ module.exports = function (request, indentation, bodyTrim, contentType) { `,open('${sanitize(filesrc, request.body.mode, bodyTrim)}','rb'),` + `'${contentType}'))`; }); - requestBody = `payload={${bodyDataMap.join(',\n')}}\nfiles=[\n${bodyFileMap.join(',\n')}\n]\n`; + requestBody = `payload = {${bodyDataMap.join(',\n')}}\nfiles=[\n${bodyFileMap.join(',\n')}\n]\n`; } else { - requestBody = 'payload={}\nfiles={}\n'; + requestBody = 'payload = {}\nfiles={}\n'; } return requestBody; case 'file': - // return `payload={open('${request.body[request.body.mode].src}', 'rb').read()\n}`; - return 'payload=""\n'; + // return `payload = {open('${request.body[request.body.mode].src}', 'rb').read()\n}`; + return 'payload = ""\n'; default: - return 'payload={}\n'; + return 'payload = {}\n'; } } - return 'payload={}\n'; + return 'payload = {}\n'; } ; diff --git a/codegens/python-requests/test/newman/newman.test.js b/codegens/python-requests/test/newman/newman.test.js index 42bc8def8..6015b59f2 100644 --- a/codegens/python-requests/test/newman/newman.test.js +++ b/codegens/python-requests/test/newman/newman.test.js @@ -14,7 +14,7 @@ describe('Convert for different types of request', function () { // Requests does not support multipart/form-data unless we are also // uploading a file. Headers are stored in a dict so we cannot have // two headers with same key - skipCollections: ['formdataCollection', 'sameNameHeadersCollection'] + skipCollections: ['formdataCollection', 'sameNameHeadersCollection', 'unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); }); @@ -31,7 +31,7 @@ describe('Convert for different types of request', function () { // Requests does not support multipart/form-data unless we are also // uploading a file. Headers are stored in a dict so we cannot have // two headers with same key - skipCollections: ['formdataCollection', 'sameNameHeadersCollection'] + skipCollections: ['formdataCollection', 'sameNameHeadersCollection', 'unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); diff --git a/codegens/python-requests/test/unit/converter.test.js b/codegens/python-requests/test/unit/converter.test.js index 2436fcb65..59df4dff7 100644 --- a/codegens/python-requests/test/unit/converter.test.js +++ b/codegens/python-requests/test/unit/converter.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), mainCollection = require('./fixtures/sample_collection.json'), convert = require('../../lib/index').convert; @@ -11,7 +11,7 @@ describe('Python- Requests converter', function () { it('should not have allow_redirects=False twice in generated snippet when' + ' followRedirect option is set as false', function () { - var request = new sdk.Request(mainCollection.item[0].request), + var request = new Request(mainCollection.item[0].request), options = { followRedirect: false, requestTimeout: 0 }; convert(request, options, function (err, snippet) { if (err) { @@ -24,7 +24,7 @@ describe('Python- Requests converter', function () { it('should have correct boolean value for allow_redirects(False, uppercased F) in generated snippet when' + ' followRedirect option is set as false', function () { - var request = new sdk.Request(mainCollection.item[0].request), + var request = new Request(mainCollection.item[0].request), options = { followRedirect: false }; convert(request, options, function (err, snippet) { if (err) { @@ -37,7 +37,7 @@ describe('Python- Requests converter', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -64,7 +64,7 @@ describe('Python- Requests converter', function () { }); it('should convert JSON tokens into appropriate python tokens', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [ { @@ -102,7 +102,7 @@ describe('Python- Requests converter', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { diff --git a/codegens/python-requests/test/unit/fixtures/sample_collection.json b/codegens/python-requests/test/unit/fixtures/sample_collection.json index 4d2aa2698..1968858e8 100644 --- a/codegens/python-requests/test/unit/fixtures/sample_collection.json +++ b/codegens/python-requests/test/unit/fixtures/sample_collection.json @@ -1421,13 +1421,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1441,13 +1439,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": "" @@ -1461,13 +1457,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, diff --git a/codegens/r-httr/.gitignore b/codegens/r-httr/.gitignore new file mode 100644 index 000000000..3040212d5 --- /dev/null +++ b/codegens/r-httr/.gitignore @@ -0,0 +1,47 @@ +.DS_Store +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + +# Coverage directory used by tools like istanbul +.coverage + +# node-waf configuration +.lock-wscript + + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ diff --git a/codegens/r-httr/.npmignore b/codegens/r-httr/.npmignore new file mode 100644 index 000000000..79ad2ba5f --- /dev/null +++ b/codegens/r-httr/.npmignore @@ -0,0 +1,76 @@ +### NPM Specific: Disregard recursive project files +### =============================================== +/.editorconfig +/.gitmodules +/test + +### Borrowed from .gitignore +### ======================== + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +snippet.swift + +out/ diff --git a/codegens/r-httr/README.md b/codegens/r-httr/README.md new file mode 100644 index 000000000..e6903f5e1 --- /dev/null +++ b/codegens/r-httr/README.md @@ -0,0 +1,42 @@ + +> Converts Postman-SDK Request into code snippet for . + +#### Prerequisites +To run Code-Gen, ensure that you have NodeJS >= v8. A copy of the NodeJS installable can be downloaded from https://nodejs.org/en/download/package-manager. + +## Using the Module +The module will expose an object which will have property `convert` which is the function for converting the Postman-SDK request to r httr snippet. + +### convert function +Convert function takes three parameters + +* `request` - Postman-SDK Request Object + +* `options` - options is an object which hsa following properties + * `indentType` - String denoting type of indentation for code snippet. eg: 'Space', 'Tab' + * `indentCount` - The number of indentation characters to add per code level + * `trimRequestBody` - Whether or not request body fields should be trimmed + +* `callback` - callback function with first parameter as error and second parameter as string for code snippet + +##### Example: +```js +var request = new sdk.Request('www.google.com'), //using postman sdk to create request + options = { + indentCount: 3, + indentType: 'Space', + requestTimeout: 200, + trimRequestBody: true + }; +convert(request, options, function(error, snippet) { + if (error) { + // handle error + } + // handle snippet +}); +``` +### Guidelines for using generated snippet + +* Since Postman-SDK Request object doesn't provide complete path of the file, it needs to be manually inserted in case of uploading a file. + +* This module doesn't support cookies. \ No newline at end of file diff --git a/codegens/r-httr/index.js b/codegens/r-httr/index.js new file mode 100644 index 000000000..bb0a047c4 --- /dev/null +++ b/codegens/r-httr/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/codegens/r-httr/lib/index.js b/codegens/r-httr/lib/index.js new file mode 100644 index 000000000..76e77c598 --- /dev/null +++ b/codegens/r-httr/lib/index.js @@ -0,0 +1,4 @@ +module.exports = { + convert: require('./rHttr').convert, + getOptions: require('./rHttr').getOptions +}; diff --git a/codegens/r-httr/lib/lodash.js b/codegens/r-httr/lib/lodash.js new file mode 100644 index 000000000..5be147afd --- /dev/null +++ b/codegens/r-httr/lib/lodash.js @@ -0,0 +1,455 @@ +/* istanbul ignore next */ +module.exports = { + + /** + * Checks if `value` is an empty object, array or string. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Values such as strings, arrays are considered empty if they have a `length` of `0`. + * + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * isEmpty(null) + * // => true + * + * isEmpty(true) + * // => true + * + * isEmpty(1) + * // => true + * + * isEmpty([1, 2, 3]) + * // => false + * + * isEmpty('abc') + * // => false + * + * isEmpty({ 'a': 1 }) + * // => false + */ + isEmpty: function (value) { + // eslint-disable-next-line lodash/prefer-is-nil + if (value === null || value === undefined) { + return true; + } + if (Array.isArray(value) || typeof value === 'string' || typeof value.splice === 'function') { + return !value.length; + } + + for (const key in value) { + if (Object.prototype.hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + }, + + /** + * Checks if `value` is `undefined`. + * + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * isUndefined(void 0) + * // => true + * + * isUndefined(null) + * // => false + */ + isUndefined: function (value) { + return value === undefined; + }, + + /** + * Checks if `func` is classified as a `Function` object. + * + * @param {*} func The value to check. + * @returns {boolean} Returns `true` if `func` is a function, else `false`. + * @example + * + * isFunction(self.isEmpty) + * // => true + * + * isFunction(/abc/) + * // => false + */ + isFunction: function (func) { + return typeof func === 'function'; + }, + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * capitalize('FRED') + * // => 'Fred' + * + * capitalize('john') + * // => 'John' + */ + + capitalize: function (string) { + return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + }, + + /** + * Reduces `array` to a value which is the accumulated result of running + * each element in `array` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `array` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, array). + * + * @param {Array} array The Array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @example + * + * reduce([1, 2], (sum, n) => sum + n, 0) + * // => 3 + * + */ + reduce: function (array, iteratee, accumulator) { + return array.reduce(iteratee, accumulator); + }, + + /** + * Iterates over elements of `array`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function|object} predicate The function/object invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * filter(users, ({ active }) => active) + * // => object for ['barney'] + */ + filter: function (array, predicate) { + if (typeof predicate === 'function') { + return array.filter(predicate); + } + var key = Object.keys(predicate), + val = predicate[key], + res = []; + array.forEach(function (item) { + if (item[key] && item[key] === val) { + res.push(item); + } + }); + return res; + }, + + /** + * The opposite of `filter` this method returns the elements of `array` + * that `predicate` does **not** return truthy for. + * + * @param {Array} array collection to iterate over. + * @param {String} predicate The String that needs to have truthy value, invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * reject(users, 'active') + * // => object for ['fred'] + */ + reject: function (array, predicate) { + var res = []; + array.forEach((object) => { + if (!object[predicate]) { + res.push(object); + } + }); + return res; + }, + + /** + * Creates an array of values by running each element of `array` thru `iteratee`. + * The iteratee is invoked with three arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n + * } + * + * map([4, 8], square) + * // => [16, 64] + */ + map: function (array, iteratee) { + return array.map(iteratee); + }, + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @example + * + * forEach([1, 2], value => console.log(value)) + * // => Logs `1` then `2`. + * + * forEach({ 'a': 1, 'b': 2 }, (value, key) => console.log(key)) + * // => Logs 'a' then 'b' + */ + + forEach: function (collection, iteratee) { + if (collection === null) { + return null; + } + + if (Array.isArray(collection)) { + return collection.forEach(iteratee); + } + const iterable = Object(collection), + props = Object.keys(collection); + var index = -1, + key, i; + + for (i = 0; i < props.length; i++) { + key = props[++index]; + iteratee(iterable[key], key, iterable); + } + return collection; + }, + + /** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise it checks if the `value` is present + * as a key in a `collection` object. + * + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ + includes: function (collection, value) { + if (Array.isArray(collection) || typeof collection === 'string') { + return collection.includes(value); + } + for (var key in collection) { + if (collection.hasOwnProperty(key)) { + if (collection[key] === value) { + return true; + } + } + } + return false; + }, + + /** + * Gets the size of `collection` by returning its length for array and strings. + * For objects it returns the number of enumerable string keyed + * properties. + * + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * size([1, 2, 3]) + * // => 3 + * + * size({ 'a': 1, 'b': 2 }) + * // => 2 + * + * size('pebbles') + * // => 7 + */ + size: function (collection) { + // eslint-disable-next-line lodash/prefer-is-nil + if (collection === null || collection === undefined) { + return 0; + } + if (Array.isArray(collection) || typeof collection === 'string') { + return collection.length; + } + + return Object.keys(collection).length; + }, + + /** + * Converts all elements in `array` into a string separated by `separator`. + * + * @param {Array} array The array to convert. + * @param {string} [separator=','] The element separator. + * @returns {string} Returns the joined string. + * @example + * + * _.join(['a', 'b', 'c'], '~'); + * // => 'a~b~c' + */ + join: function (array, separator) { + if (array === null) { + return ''; + } + return array.join(separator); + }, + + /** + * Removes trailing whitespace or specified characters from `string`. + * + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + * @example + * + * trimEnd(' abc ') + * // => ' abc' + * + * trimEnd('-_-abc-_-', '_-') + * // => '-_-abc' + */ + trimEnd: function (string, chars) { + if (!string) { + return ''; + } + if (string && !chars) { + return string.replace(/\s*$/, ''); + } + chars += '$'; + return string.replace(new RegExp(chars, 'g'), ''); + }, + + /** + * Returns the index of the first + * element `predicate` returns truthy for. + * + * @param {Array} array The array to inspect. + * @param {Object} predicate The exact object to be searched for in the array. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * _.findIndex(users, {'active' : false}); + * // => 0 + * + */ + findIndex: function (array, predicate) { + var length = array === null ? 0 : array.length, + index = -1, + keys = Object.keys(predicate), + found, i; + if (!length) { + return -1; + } + for (i = 0; i < array.length; i++) { + found = true; + // eslint-disable-next-line no-loop-func + keys.forEach((key) => { + if (!(array[i][key] && array[i][key] === predicate[key])) { + found = false; + } + }); + if (found) { + index = i; + break; + } + } + return index; + }, + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @param {Object} object The object to query. + * @param {string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * const object = { a: {b : 'c'} } + * + * + * get(object, 'a.b.c', 'default') + * // => 'default' + * + * get(object, 'a.b', 'default') + * // => 'c' + */ + get: function (object, path, defaultValue) { + if (object === null) { + return undefined; + } + var arr = path.split('.'), + res = object, + i; + for (i = 0; i < arr.length; i++) { + res = res[arr[i]]; + if (res === undefined) { + return defaultValue; + } + } + return res; + }, + + /** + * Checks if `predicate` returns truthy for **all** elements of `array`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * every([true, 1, null, 'yes'], Boolean) + * // => false + */ + every: function (array, predicate) { + var index = -1, + length = array === null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + +}; diff --git a/codegens/r-httr/lib/options.js b/codegens/r-httr/lib/options.js new file mode 100644 index 000000000..f0d3b9e21 --- /dev/null +++ b/codegens/r-httr/lib/options.js @@ -0,0 +1,50 @@ +const options = [ + { + name: 'Set indentation count', + id: 'indentCount', + type: 'positiveInteger', + default: 2, + description: + 'Set the number of indentation characters to add per code level' + }, + { + name: 'Set indentation type', + id: 'indentType', + type: 'enum', + availableOptions: ['Tab', 'Space'], + default: 'Space', + description: 'Select the character used to indent lines of code' + }, + { + name: 'Trim request body fields', + id: 'trimRequestBody', + type: 'boolean', + default: false, + description: + 'Remove white space and additional lines that may affect the server\'s response' + }, + { + name: 'Set request timeout', + id: 'requestTimeout', + type: 'positiveInteger', + default: 0, + description: + 'Set number of milliseconds the request should wait for a response' + + ' before timing out (use 0 for infinity)' + } +]; + +/** + * Used in order to get options for generation of r-httr code snippet + * + * @module getOptions + * + * @returns {Array} Options specific to generation of r-httr code snippet + */ +function getOptions () { + return options; +} + +module.exports = { + getOptions +}; diff --git a/codegens/r-httr/lib/rHttr.js b/codegens/r-httr/lib/rHttr.js new file mode 100644 index 000000000..3cde04a94 --- /dev/null +++ b/codegens/r-httr/lib/rHttr.js @@ -0,0 +1,278 @@ +const getOptions = require('./options').getOptions, + sanitizeString = require('./util/sanitize').sanitizeString, + sanitizeOptions = require('./util/sanitize').sanitizeOptions, + parseBody = require('./util/parseBody').parseBody; + +/** + * Returns the snippet header + * + * @module convert + * + * @returns {string} the snippet headers (uses) + */ +function getSnippetHeader () { + return 'library(httr)\n\n'; +} + +/** + * Returns the snippet footer + * + * @module convert + * @returns {string} the snippet headers (uses) + */ +function getSnippetFooter () { + return 'cat(content(res, \'text\'))'; +} + +/** + * Gets the defined indentation from options + * + * @param {object} options - process options + * @returns {String} - indentation characters + */ +function getIndentation (options) { + if (options && options.indentType && options.indentCount) { + let charIndentation = options.indentType === 'Tab' ? '\t' : ' '; + return charIndentation.repeat(options.indentCount); + } + return ' '; +} + +/** + * Used to get the headers and put them in the desired form of the language + * + * @param {Object} request - postman SDK-request object + * @returns {String} - request headers in the desired format + */ +function getRequestHeaders (request) { + return request.headers.members; +} + +/** + * Returns the request's url in string format + * + * @param {Object} request - postman SDK-request object + * @returns {String} - request url in string representation + */ +function getRequestURL (request) { + return request.url.toString(); +} + +/** + * Validates if the input is a function + * + * @module convert + * + * @param {*} validateFunction - postman SDK-request object + * @returns {boolean} true if is a function otherwise false + */ +function validateIsFunction (validateFunction) { + return typeof validateFunction === 'function'; +} + +/** + * Returns the request's url in string format + * + * @param {Object} request - postman SDK-request object + * @returns {String} - request url in string representation + */ +function getRequestMethod (request) { + return request.method; +} + +/** + * Transforms an array of headers into the desired form of the language + * + * @param {Array} mapToSnippetArray - array of key values + * @param {String} indentation - used for indenting snippet's structure + * @param {boolean} sanitize - whether to sanitize the key and values + * @returns {String} - array in the form of [ key => value ] + */ +function getSnippetArray (mapToSnippetArray, indentation, sanitize) { + let mappedArray = mapToSnippetArray.map((entry) => { + return `${indentation}'${sanitize ? sanitizeString(entry.key, true) : entry.key}' = ` + + `${sanitize ? '\'' + sanitizeString(entry.value) + '\'' : entry.value}`; + }); + return `c(\n${mappedArray.join(',\n')}\n)`; +} + +/** + * Groups the headers with the same key value + * + * @param {array} headers The array of headers + * @returns {array} An array with grouped headers + */ +function groupHeadersSameKey (headers) { + let grouped = {}, + groupedList = []; + headers.forEach((header) => { + if (grouped.hasOwnProperty(header.key)) { + grouped[header.key].push(header.value); + } + else { + grouped[header.key] = [header.value]; + } + }); + groupedList = Object.keys(grouped).map((key) => { + return { + key, + value: grouped[key].join(', ') + }; + }); + return groupedList; +} + +/** + * Transforms an array of headers into the desired form of the language + * + * @param {Array} headers - postman SDK-headers + * @param {String} indentation - used for indenting snippet's structure + * @returns {String} - request headers in the desired format + */ +function getSnippetHeaders (headers, indentation) { + if (headers.length > 0) { + headers = headers.filter((header) => { return !header.disabled; }); + headers = groupHeadersSameKey(headers); + return `headers = ${getSnippetArray(headers, indentation, true)}\n\n`; + } + return ''; +} + +/** + * Return an encode snippet depending on the mode + * + * @param {object} mode - The body mode from request + * @returns {string} the encode snippet + */ +function getEncodeSnippetByMode (mode) { + const isForm = ['urlencoded'].includes(mode), + isMultipart = ['formdata'].includes(mode); + let snippet = ''; + if (isForm) { + snippet = ', encode = \'form\''; + } + else if (isMultipart) { + snippet = ', encode = \'multipart\''; + } + + return snippet; +} + +/** + * Creates the snippet request for the postForm method + * + * @module convert + * + * @param {string} url - string url of the service + * @param {boolean} hasParams - wheter or not include the params + * @param {boolean} hasHeaders - wheter or not include the headers + * @param {string} methodUC - The method upper cased + * @param {string} mode - the request body mode + * @param {number} requestTimeout - The request timeout in the options + * @returns {String} - returns generated snippet + */ +function getSnippetFromMethod (url, hasParams, hasHeaders, methodUC, mode, requestTimeout = 0) { + let paramsSnippet = hasParams ? ', body = body' : '', + headersSnippet = hasHeaders ? ', add_headers(headers)' : '', + encodeSnippet = getEncodeSnippetByMode(mode), + timeoutSnippet = requestTimeout ? `, timeout(${requestTimeout})` : ''; + + return `res <- VERB("${methodUC}", url = "${url}"` + + `${paramsSnippet}${headersSnippet}${encodeSnippet}${timeoutSnippet})\n\n`; +} + +/** + * Creates the snippet request for either get ulr or post form + * + * @module convert + * + * @param {object} requestData - an object that includes: + * {string} method - request http method + * {boolean} hasParams - wheter or not include the params + * {boolean} hasHeaders - wheter or not include the headers + * {string} mode - the request body mode + * {number} requestTimeout - The request timeout from the options + * @returns {String} - returns generated snippet + */ +function getSnippetRequest ({url, method, hasParams, hasHeaders, mode, requestTimeout}) { + const methodUC = method.toUpperCase(); + let snippetRequest = ''; + if (methodUC && methodUC !== '') { + snippetRequest = getSnippetFromMethod(url, hasParams, hasHeaders, methodUC, mode, requestTimeout); + } + return snippetRequest; +} + +/** + * Gets the defined body trim from options + * + * @param {object} options - process options + * @returns {boolea} - wheter to trim the request body + */ +function getBodyTrim (options) { + if (options && options.trimRequestBody) { + return options.trimRequestBody; + } + return false; +} + +/** + * Used to convert the postman sdk-request object in PHP-Guzzle request snippet + * + * @module convert + * + * @param {Object} request - postman SDK-request object + * @param {object} options - process options + * @param {Function} callback - function with parameters (error, snippet) + * @returns {String} - returns generated PHP-Guzzle snippet via callback + */ +function convert (request, options, callback) { + + if (!validateIsFunction(callback)) { + throw new Error('R-Httr~convert: Callback is not a function'); + } + let snippet = ''; + options = sanitizeOptions(options, getOptions()); + + const method = getRequestMethod(request), + indentation = getIndentation(options), + url = getRequestURL(request), + mode = request.body ? request.body.mode : '', + snippetHeaders = getSnippetHeaders(getRequestHeaders(request), indentation), + snippetHeader = getSnippetHeader(), + snippetFooter = getSnippetFooter(), + snippetbody = parseBody(request.body, indentation, getBodyTrim(options), request.headers.get('Content-Type')), + snippetRequest = getSnippetRequest({ + url: url, + method: method, + hasParams: snippetbody !== '', + hasHeaders: snippetHeaders !== '', + mode: mode, + requestTimeout: options.requestTimeout + }); + + snippet += snippetHeader; + snippet += snippetHeaders; + snippet += snippetbody; + snippet += snippetRequest; + snippet += snippetFooter; + + return callback(null, snippet); +} + +module.exports = { + /** + * Used in order to get options for generation of R-rCurl code snippet + * + * @module getOptions + * + * @returns {Array} Options specific to generation of R-rCurl code snippet + */ + getOptions, + + convert, + getSnippetHeaders, + getSnippetFromMethod, + getSnippetRequest, + getIndentation +}; diff --git a/codegens/r-httr/lib/util/parseBody.js b/codegens/r-httr/lib/util/parseBody.js new file mode 100644 index 000000000..96f57466c --- /dev/null +++ b/codegens/r-httr/lib/util/parseBody.js @@ -0,0 +1,286 @@ +const sanitizeString = require('./sanitize').sanitizeString, + _ = require('../lodash'); + +/** + * + * @param {Array} array - form data array + * @param {String} key - key of form data param + * @param {String} type - type of form data param(file/text) + * @param {String} val - value/src property of form data param + * @param {String} disabled - Boolean denoting whether the param is disabled or not + * @param {String} contentType - content type header of the param + * + * Appends a single param to form data array + */ +function addFormParam (array, key, type, val, disabled, contentType) { + if (type === 'file') { + array.push({ + key: key, + type: type, + src: val, + disabled: disabled, + contentType: contentType + }); + } + else { + array.push({ + key: key, + type: type, + value: val, + disabled: disabled, + contentType: contentType + }); + } +} + +/** +* parses a body to the corresponding snippet +* +* @param {object} body - postman request body +* @returns {String} +*/ +function solveMultiFile (body) { + if (body && body.mode === 'formdata') { + let formdata = body.formdata, + formdataArray = []; + formdata.members.forEach((param) => { + let key = param.key, + type = param.type, + disabled = param.disabled, + contentType = param.contentType; + // check if type is file or text + if (type === 'file') { + // if src is not of type string we check for array(multiple files) + if (typeof param.src !== 'string') { + // if src is an array(not empty), iterate over it and add files as separate form fields + if (Array.isArray(param.src) && param.src.length) { + param.src.forEach((filePath) => { + addFormParam(formdataArray, key, param.type, filePath, disabled, contentType); + }); + } + // if src is not an array or string, or is an empty array, add a placeholder for file path(no files case) + else { + addFormParam(formdataArray, key, param.type, '/path/to/file', disabled, contentType); + } + } + // if src is string, directly add the param with src as filepath + else { + addFormParam(formdataArray, key, param.type, param.src, disabled, contentType); + } + } + // if type is text, directly add it to formdata array + else { + addFormParam(formdataArray, key, param.type, param.value, disabled, contentType); + } + }); + + body.update({ + mode: 'formdata', + formdata: formdataArray + }); + } + return body; +} + +/** + * Parses Raw data + * + * @param {Object} body body object from request. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @param {String} contentType Content type of the body being sent + * @returns {String} snippet of the body generation + */ +function parseRawBody (body, indentation, bodyTrim, contentType) { + let bodySnippet = ''; + if (contentType && (contentType === 'application/json' || contentType.match(/\+json$/))) { + try { + let jsonBody = JSON.parse(body); + bodySnippet += `'${JSON.stringify(jsonBody, null, indentation.length)}';`; + } + catch (error) { + bodySnippet += `"${sanitizeString(body.toString(), bodyTrim)}"`; + } + } + else { + bodySnippet += JSON.stringify(body.toString()); + } + return bodySnippet; +} + +/** + * Parses URL encoded body + * + * @param {Object} body body object from request. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @returns {String} snippet of the body generation + */ +function parseURLEncodedBody (body, indentation, bodyTrim) { + let enabledBodyList = _.reject(body.members, 'disabled'), + bodySnippet = ''; + if (!_.isEmpty(enabledBodyList)) { + let bodyDataMap = _.map(enabledBodyList, (data) => { + return `${indentation}'${sanitizeString(data.key, bodyTrim)}' = '${sanitizeString(data.value, bodyTrim)}'`; + }); + bodySnippet += `list(\n${bodyDataMap.join(',\n')}\n)`; + } + return bodySnippet; +} + +/** + * Takes in a key value form data and creates the PHP guzzle structure + * + * @param {Object} data item from the array of form data (key value). + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @returns {String} snippet of the body generation + */ +function buildFormDataParam (data, indentation, bodyTrim) { + return `${indentation}'${sanitizeString(data.key, bodyTrim)}' = '${sanitizeString(data.value, bodyTrim)}'`; +} + +/** + * Gets the content file of the param + * + * @param {Object} data item from the array of form data (key value). + * @returns {String} snippet of the content + */ +function getContentFileFormData (data) { + if (!data.value) { + if (Array.isArray(data.src)) { + return data.src.length === 0 ? 'path/to/file' : data.src.join('/'); + } + return data.src ? data.src : '/path/to/file'; + } + return `'${sanitizeString(data.value, bodyTrim)}'`; +} + +/** + * Takes in a key value form data and creates the PHP guzzle structure + * for files + * + * @param {Object} data item from the array of form data (key value). + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @returns {String} snippet of the body generation + */ +function buildFormDataParamFile (data, indentation, bodyTrim) { + return `${indentation}'${sanitizeString(data.key, bodyTrim)}' = upload_file('${getContentFileFormData(data)}')`; +} + +/** + * builds a data param + * + * @param {Object} body body object from request. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @returns {String} snippet of the body generation + */ +function parseFormData (body, indentation, bodyTrim) { + let enabledBodyList = _.reject(body.members, 'disabled'), + bodySnippet = ''; + if (!_.isEmpty(enabledBodyList)) { + let bodyDataMap = _.map(enabledBodyList, (data) => { + if (data.type === 'file') { + return buildFormDataParamFile(data, indentation, bodyTrim); + } + return buildFormDataParam(data, indentation, bodyTrim); + }); + bodySnippet += `list(\n${bodyDataMap.join(',\n')}\n)`; + } + return bodySnippet; +} + +/** + * Parses Body of file + * + * @return {String} the data for a binary file + */ +function parseFromFile () { + return '\'\''; +} + +/** + * Parses Body of graphql + * + * @param {Object} body body object from request. + * @param {boolean} bodyTrim trim body option + * @return {String} the data for a binary file + */ +function parseGraphQL (body, bodyTrim) { + const query = body.query; + let bodySnippet = '', + graphqlVariables; + try { + graphqlVariables = JSON.parse(body.variables); + } + catch (e) { + graphqlVariables = {}; + } + bodySnippet = `'${sanitizeString(JSON.stringify({ + query: query, + variables: graphqlVariables + }), bodyTrim)}'`; + return bodySnippet; +} + +/** + * Parses Body from the Request + * + * @param {Object} body body object from request. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @param {String} contentType Content type of the body being sent + */ +function processBodyModes (body, indentation, bodyTrim, contentType) { + let bodySnippet = ''; + switch (body.mode) { + case 'urlencoded': { + bodySnippet = parseURLEncodedBody(body.urlencoded, indentation, bodyTrim); + return bodySnippet === '' ? '' : `body = ${bodySnippet}\n\n`; + } + case 'raw': { + bodySnippet = parseRawBody(body.raw, indentation, bodyTrim, contentType); + return bodySnippet === '' ? '' : `body = ${bodySnippet}\n\n`; + } + case 'graphql': { + bodySnippet = parseGraphQL(body.graphql, bodyTrim); + return bodySnippet === '' ? '' : `body = ${bodySnippet}\n\n`; + } + case 'formdata': { + bodySnippet = parseFormData(body.formdata, indentation, bodyTrim); + return bodySnippet === '' ? '' : `body = ${bodySnippet}\n\n`; + } + case 'file': { + bodySnippet = parseFromFile(); + return bodySnippet === '' ? '' : `body = upload_file(${bodySnippet})\n\n`; + } + default: { + bodySnippet = parseRawBody(body.raw, indentation, bodyTrim, contentType); + return bodySnippet === '' ? '' : `body = ${bodySnippet}\n\n`; + } + } +} + +/** +* parses a body to the corresponding snippet +* +* @param {object} body - postman request body +* @param {string} indentation - indentation character +* @param {boolean} bodyTrim trim body option +* @param {String} contentType Content type of the body being sent +* @returns {String} snippet of the body generation +*/ +function parseBody (body, indentation, bodyTrim, contentType) { + let snippet = ''; + if (body && !_.isEmpty(body)) { + body = solveMultiFile(body); + return processBodyModes(body, indentation, bodyTrim, contentType); + } + return snippet; +} + +module.exports = { + parseBody +}; diff --git a/codegens/r-httr/lib/util/sanitize.js b/codegens/r-httr/lib/util/sanitize.js new file mode 100644 index 000000000..7bb1c3615 --- /dev/null +++ b/codegens/r-httr/lib/util/sanitize.js @@ -0,0 +1,89 @@ +module.exports = { + /** + * used to sanitize eg: trim, handle escape characters + * + * @param {String} inputString - input + * @param {Boolean} inputTrim - whether to trim the input + * @returns {String} + */ + sanitizeString: function (inputString, inputTrim) { + if (typeof inputString !== 'string') { + return ''; + } + inputString = inputTrim && typeof inputTrim === 'boolean' ? inputString.trim() : inputString; + return inputString + .replace(/\\/g, '\\\\') + .replace(/'/g, '\\\''); + }, + + /** + * sanitizes input options + * + * @param {Object} options - Options provided by the user + * @param {Array} optionsArray - options array received from getOptions function + * + * @returns {Object} - Sanitized options object + */ + sanitizeOptions: function (options, optionsArray) { + var result = {}, + defaultOptions = {}, + id; + + optionsArray.forEach((option) => { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + + for (id in options) { + if (options.hasOwnProperty(id)) { + if (defaultOptions[id] === undefined) { + continue; + } + switch (defaultOptions[id].type) { + case 'boolean': + if (typeof options[id] !== 'boolean') { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'positiveInteger': + if (typeof options[id] !== 'number' || options[id] < 0) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'enum': + if (!defaultOptions[id].availableOptions.includes(options[id])) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + default: + result[id] = options[id]; + } + } + } + + for (id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + if (result[id] === undefined) { + result[id] = defaultOptions[id].default; + } + } + } + + return result; + } + +}; diff --git a/codegens/r-httr/npm-shrinkwrap.json b/codegens/r-httr/npm-shrinkwrap.json new file mode 100644 index 000000000..c61f6f30b --- /dev/null +++ b/codegens/r-httr/npm-shrinkwrap.json @@ -0,0 +1,5 @@ +{ + "name": "@postman/codegen-r-httr", + "version": "0.0.1", + "lockfileVersion": 1 +} diff --git a/codegens/r-httr/npm/test-lint.js b/codegens/r-httr/npm/test-lint.js new file mode 100644 index 000000000..2f4db0cb8 --- /dev/null +++ b/codegens/r-httr/npm/test-lint.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +var shell = require('shelljs'), + chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/r-httr/npm/test-newman.js b/codegens/r-httr/npm/test-newman.js new file mode 100644 index 000000000..0c8559a8e --- /dev/null +++ b/codegens/r-httr/npm/test-newman.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'newman'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running newman tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/r-httr/npm/test-unit.js b/codegens/r-httr/npm/test-unit.js new file mode 100644 index 000000000..b93a5cba1 --- /dev/null +++ b/codegens/r-httr/npm/test-unit.js @@ -0,0 +1,60 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running unit tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + return undefined; + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/r-httr/npm/test.js b/codegens/r-httr/npm/test.js new file mode 100644 index 000000000..07be3d9ca --- /dev/null +++ b/codegens/r-httr/npm/test.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node +var chalk = require('chalk'), + exit = require('shelljs').exit, + prettyms = require('pretty-ms'), + startedAt = Date.now(), + name = require('../package.json').name; + +require('async').series([ + require('./test-lint'), + require('./test-newman'), + // Add a separate folder for every new suite of tests + require('./test-unit') + // require('./test-browser') + // require('./test-integration') +], function (code) { + // eslint-disable-next-line max-len + console.info(chalk[code ? 'red' : 'green'](`\n${name}: duration ${prettyms(Date.now() - startedAt)}\n${name}: ${code ? 'not ok' : 'ok'}!`)); + exit(code && (typeof code === 'number' ? code : 1) || 0); +}); diff --git a/codegens/r-httr/package.json b/codegens/r-httr/package.json new file mode 100644 index 000000000..942137988 --- /dev/null +++ b/codegens/r-httr/package.json @@ -0,0 +1,34 @@ +{ + "name": "@postman/codegen-r-httr", + "version": "0.0.1", + "description": "Converts Postman SDK Requests to r-httr code snippet", + "main": "index.js", + "com_postman_plugin": { + "type": "code_generator", + "lang": "R", + "variant": "httr", + "syntax_mode": "R" + }, + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "node npm/test.js", + "test-lint": "node npm/test-lint.js", + "test-newman": "node npm/test-newman.js", + "test-unit": "node npm/test-unit.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "Postman Labs ", + "license": "Apache-2.0", + "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/r-httr", + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=8" + } +} diff --git a/codegens/r-httr/test/ci-install.sh b/codegens/r-httr/test/ci-install.sh new file mode 100755 index 000000000..f4767b199 --- /dev/null +++ b/codegens/r-httr/test/ci-install.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/r-httr" +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 +sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/' +sudo apt-get update +sudo apt-get install r-base libcurl4-gnutls-dev +sudo R --vanilla -e 'install.packages("httr", version="1.4.2", repos="http://cran.us.r-project.org")' diff --git a/codegens/r-httr/test/newman/newman.test.js b/codegens/r-httr/test/newman/newman.test.js new file mode 100644 index 000000000..975e11022 --- /dev/null +++ b/codegens/r-httr/test/newman/newman.test.js @@ -0,0 +1,20 @@ +var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, + convert = require('../../index').convert; + + +describe('r-httr Converter', function () { + describe('convert for different request types', function () { + var options = { + indentType: 'Space', + indentCount: 4 + }, + testConfig = { + // filename along with the appropriate version of the file. This file will be used to run the snippet. + fileName: 'snippet.r', + // Run script required to run the generated code snippet + runScript: 'Rscript snippet.r', + skipCollections: ['redirectCollection', 'unsupportedMethods', 'rawBody'] + }; + runNewmanTest(convert, options, testConfig); + }); +}); diff --git a/codegens/r-httr/test/unit/convert.test.js b/codegens/r-httr/test/unit/convert.test.js new file mode 100644 index 000000000..35b9f72d3 --- /dev/null +++ b/codegens/r-httr/test/unit/convert.test.js @@ -0,0 +1,132 @@ +var expect = require('chai').expect, + { convert } = require('../../index'), + { Collection } = require('postman-collection/lib/collection/collection'), + fs = require('fs'), + path = require('path'); + + +describe('convert function', function () { + + it('should convert a simple get request', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[1].request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should convert a simple get request with timeout', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[1].request, { requestTimeout: 3 }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet.includes('timeout(3)')).to.be.true; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should convert a post request with formdata', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[4].request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should convert a post request with raw data', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[6].request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should convert a post request with urlencoded', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[7].request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should convert a post request with json with raw', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[8].request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should convert a post request with javascript with raw', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[9].request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should convert a post request with xml with raw', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[10].request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should convert a post request with binary file', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + convert(collection.items.members[25].request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + fs.writeFileSync(path.join(__dirname, './fixtures/snippet.r'), snippet); + }); + done(); + }); + + it('should throw an error when callback is not a function', function () { + expect(function () { convert({}, {}); }) + .to.throw('R-Httr~convert: Callback is not a function'); + }); +}); diff --git a/codegens/r-httr/test/unit/fixtures/sample_collection.json b/codegens/r-httr/test/unit/fixtures/sample_collection.json new file mode 100644 index 000000000..c003e6bf1 --- /dev/null +++ b/codegens/r-httr/test/unit/fixtures/sample_collection.json @@ -0,0 +1,1633 @@ +{ + "info": { + "name": "Code-Gen Test R HTTR", + "_postman_id": "43058ce7-94da-0f0f-366e-22b77d566316", + "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Request Headers", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "try {", + " tests[\"Body contains headers\"] = responseBody.has(\"headers\");", + " responseJSON = JSON.parse(responseBody);", + " tests[\"Header contains host\"] = \"host\" in responseJSON.headers;", + " tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;", + "}", + "catch (e) { }", + "", + "", + "", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "testing", + "value": "'singlequotes'" + }, + { + "key": "TEST", + "value": "\"doublequotes\"" + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "Request Headers (With special Characters)", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "try {", + " tests[\"Body contains headers\"] = responseBody.has(\"headers\");", + " responseJSON = JSON.parse(responseBody);", + " tests[\"Header contains host\"] = \"host\" in responseJSON.headers;", + " tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;", + "}", + "catch (e) { }", + "", + "", + "", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "TEST", + "value": "@#$%^&*()" + }, + { + "key": "more", + "value": ",./';[]}{\":?><|" + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "Request Headers with disabled headers", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "try {", + " tests[\"Body contains headers\"] = responseBody.has(\"headers\");", + " responseJSON = JSON.parse(responseBody);", + " tests[\"Header contains host\"] = \"host\" in responseJSON.headers;", + " tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;", + "}", + "catch (e) { }", + "", + "", + "", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "not-disabled-header", + "value": "ENABLED" + }, + { + "key": "disabled header", + "value": "DISABLED", + "disabled": true + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "GET Request with disabled query", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "tests['response json contains headers'] = _.has(responseJSON, 'headers');", + "tests['response json contains args'] = _.has(responseJSON, 'args');", + "tests['response json contains url'] = _.has(responseJSON, 'url');", + "", + "tests['args key contains argument passed as url parameter'] = ('test' in responseJSON.args);", + "tests['args passed via request url params has value \"123\"'] = (_.get(responseJSON, 'args.test') === \"123\");" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/get?test=123&anotherone=232", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "test", + "value": "123", + "equals": true + }, + { + "key": "anotherone", + "value": "232", + "equals": true + }, + { + "key": "anotheroneone", + "value": "sdfsdf", + "equals": true, + "disabled": true + } + ] + }, + "description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested." + }, + "response": [] + }, + { + "name": "POST form data with special characters Copy", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "pl", + "value": "'a'", + "type": "text" + }, + { + "key": "qu", + "value": "\"b\"", + "type": "text" + }, + { + "key": "hdjkljh ", + "value": "c ", + "type": "text" + }, + { + "key": "sa", + "value": "d", + "type": "text" + }, + { + "key": "Special ", + "value": "!@#$%&*()^_+=`~ ", + "type": "text" + }, + { + "key": "Not Select", + "value": "Disabled", + "type": "text", + "disabled": true + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Resolve URL (Quotes + Special Characters) Copy", + "request": { + "method": "POST", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/:action?a=!@$^*()_-`%26&b=,./';[]}{\":/?><||\\", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + ":action" + ], + "query": [ + { + "key": "a", + "value": "!@$^*()_-`%26", + "equals": true + }, + { + "key": "b", + "value": ",./';[]}{\":/?><||\\", + "equals": true + } + ], + "variable": [ + { + "key": "action", + "value": "post" + } + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "POST Raw Text", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST urlencoded data with disabled entries", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "1", + "value": "'a'", + "description": "", + "type": "text", + "disabled": true + }, + { + "key": "2", + "value": "\"b\"", + "description": "", + "type": "text", + "disabled": true + }, + { + "key": "'3'", + "value": "c", + "description": "", + "type": "text" + }, + { + "key": "\"4\" ", + "value": "d ", + "description": "", + "type": "text" + }, + { + "key": "Special", + "value": "!@#$%&*()^_=`~", + "description": "", + "type": "text" + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\", + "description": "", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST json with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [ + { + "id": "0bfab08c-cc5a-4bb0-85f2-699383707fe4", + "name": "POST json with raw", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "Lets a server whitelist headers that browsers are allowed to access." + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Length", + "value": "385", + "name": "Content-Length", + "description": "The length of the response body in octets (8-bit bytes)" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Wed, 07 Feb 2018 10:06:15 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "ETag", + "value": "W/\"215-u7EU1nFtauIn0/aVifjuXA\"", + "name": "ETag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Vary", + "value": "X-HTTP-Method-Override, Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + }, + { + "key": "set-cookie", + "value": "sails.sid=s%3AxRBxgrc9M-jKK_l1mX3y3rM_ry8wYLz4.Of4qpOzd9hi6uO0sAQIj%2Bxs2VeppWxYjJa4OpIW3PKg; Path=/; HttpOnly", + "name": "set-cookie", + "description": "an HTTP cookie" + } + ], + "cookie": [ + { + "expires": "Tue Jan 19 2038 08:44:07 GMT+0530 (IST)", + "httpOnly": true, + "domain": "postman-echo.com", + "path": "/", + "secure": false, + "value": "s%3AxRBxgrc9M-jKK_l1mX3y3rM_ry8wYLz4.Of4qpOzd9hi6uO0sAQIj%2Bxs2VeppWxYjJa4OpIW3PKg", + "key": "sails.sid" + } + ], + "responseTime": null, + "body": "{\"args\":{},\"data\":\"{\\n \\\"json\\\": \\\"Test-Test\\\"\\n}\",\"files\":{},\"form\":{},\"headers\":{\"host\":\"postman-echo.com\",\"content-length\":\"25\",\"accept\":\"*/*\",\"accept-encoding\":\"gzip, deflate\",\"cache-control\":\"no-cache\",\"content-type\":\"text/plain\",\"cookie\":\"sails.sid=s%3AkOgtF1XmXtVFx-Eg3S7-37BKKaMqMDPe.hnwldNwyvsaASUiRR0Y0vcowadkMXO4HMegTeVIPgqo\",\"postman-token\":\"2ced782f-a141-428e-8af6-04ce954a77d5\",\"user-agent\":\"PostmanRuntime/7.1.1\",\"x-forwarded-port\":\"443\",\"x-forwarded-proto\":\"https\"},\"json\":null,\"url\":\"https://postman-echo.com/post\"}" + } + ] + }, + { + "name": "POST javascript with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/javascript" + } + ], + "body": { + "mode": "raw", + "raw": "var val = 6;\nconsole.log(val);console.log('$text\r\n');\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/xml with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/xml" + } + ], + "body": { + "mode": "raw", + "raw": "\n\tTest Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/html with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/html" + } + ], + "body": { + "mode": "raw", + "raw": "\n Test Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Resolve URL", + "request": { + "method": "POST", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/:action?a=''&b=\"\"", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + ":action" + ], + "query": [ + { + "key": "a", + "value": "''", + "equals": true + }, + { + "key": "b", + "value": "\"\"", + "equals": true + }, + { + "key": "more", + "value": "", + "equals": true, + "disabled": true + } + ], + "variable": [ + { + "key": "action", + "value": "post" + } + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PUT Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + }, + "description": "The HTTP `PUT` request method is similar to HTTP `POST`. It too is meant to \ntransfer data to a server (and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `PUT` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following \nraw HTTP request,\n\n> PUT /hi/there?hand=wave\n>\n> \n\n\n" + }, + "response": [] + }, + { + "name": "PATCH Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." + }, + "url": { + "raw": "https://postman-echo.com/patch", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "patch" + ] + }, + "description": "The HTTP `PATCH` method is used to update resources on a server. The exact\nuse of `PATCH` requests depends on the server in question. There are a number\nof server implementations which handle `PATCH` differently. Technically, \n`PATCH` supports both Query String parameters and a Request Body.\n\nThis endpoint accepts an HTTP `PATCH` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "DELETE Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Donec fermentum, nisi sed cursus eleifend, nulla tortor ultricies tellus, ut vehicula orci arcu ut velit. In volutpat egestas dapibus.\nMorbi condimentum vestibulum sapien. Etiam dignissim diam quis eros lobortis gravida vel lobortis est. Etiam gravida sed." + }, + "url": { + "raw": "https://postman-echo.com/delete", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "delete" + ] + }, + "description": "The HTTP `DELETE` method is used to delete resources on a server. The exact\nuse of `DELETE` requests depends on the server implementation. In general, \n`DELETE` requests support both, Query String parameters as well as a Request \nBody.\n\nThis endpoint accepts an HTTP `DELETE` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "OPTIONS to postman echo", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "OPTIONS", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "LINK request", + "request": { + "method": "LINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "UNLINK request", + "request": { + "method": "UNLINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "LOCK request", + "request": { + "method": "LOCK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "UNLOCK request", + "request": { + "method": "UNLOCK", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PROPFIND request", + "request": { + "method": "PROPFIND", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "VIEW request", + "request": { + "method": "VIEW", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PURGE Request", + "request": { + "method": "PURGE", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "COPY Request", + "request": { + "method": "COPY", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + }, + "description": "" + }, + "response": [ + { + "id": "47494bfb-6d21-4f1f-ace0-f88e5e1ac05d", + "name": "COPY Request", + "originalRequest": { + "method": "COPY", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "_postman_previewtype": "text", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "Lets a server whitelist headers that browsers are allowed to access." + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Length", + "value": "59", + "name": "Content-Length", + "description": "The length of the response body in octets (8-bit bytes)" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Mon, 05 Feb 2018 07:48:41 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "ETag", + "value": "W/\"af-MmpVeTvfnSW88c4riXD0uw\"", + "name": "ETag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Vary", + "value": "Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + } + ], + "cookie": [], + "responseTime": 378, + "body": "{\n \"status\": 200,\n \"method\": \"COPY\"\n}" + } + ] + }, + { + "name": "POST binary file", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/x-www-form-urlencoded", + "type": "text" + } + ], + "body": { + "mode": "file", + "file": {} + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "POST form data with file", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "test-file", + "type": "file", + "src": "" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "POST graphql body(json) with raw", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "graphql", + "graphql": { + "query": "{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: \"performers\"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}", + "variables": "{\n\t\"variable_key\": \"variable_value\"\n}" + } + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "POST empty Formdata", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "Multiple headers with same name", + "request": { + "method": "GET", + "header": [ + { + "key": "key", + "value": "value1", + "type": "text" + }, + { + "key": "key", + "value": "value2", + "type": "text" + } + ], + "url": { + "raw": "https://postman-echo.com/get", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + }, + { + "name": "POST multipart/form-data with text parameters", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "pl", + "value": "'a'", + "type": "text" + }, + { + "key": "qu", + "value": "\"b\"", + "type": "text" + }, + { + "key": "sa", + "value": "d", + "type": "text" + }, + { + "key": "Special", + "value": "!@#$%&*()^_+=`~", + "type": "text" + }, + { + "key": "Not Select", + "value": "Disabled", + "type": "text", + "disabled": true + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + } + ] +} diff --git a/codegens/r-httr/test/unit/options.test.js b/codegens/r-httr/test/unit/options.test.js new file mode 100644 index 000000000..8e80ce7ad --- /dev/null +++ b/codegens/r-httr/test/unit/options.test.js @@ -0,0 +1,28 @@ +var expect = require('chai').expect, + getOptions = require('../../index').getOptions, + availableOptions = [{ + 0: 'indentCount' + }, + { + 1: 'indentType' + }, + { + 2: 'trimRequestBody' + }, + { + 3: 'requestTimeout' + } + ]; + +describe('getOptions function', function () { + it('should return array of options for R-HTTR converter', function () { + expect(getOptions()).to.be.an('array'); + }); + + it('should return all the valid options', function () { + let options = getOptions(); + availableOptions.forEach((availableOption, index) => { + expect(options[index]).to.have.property('id', availableOption[index]); + }); + }); +}); diff --git a/codegens/r-httr/test/unit/parseBody.test.js b/codegens/r-httr/test/unit/parseBody.test.js new file mode 100644 index 000000000..52532e705 --- /dev/null +++ b/codegens/r-httr/test/unit/parseBody.test.js @@ -0,0 +1,133 @@ +var expect = require('chai').expect, + { Collection } = require('postman-collection/lib/collection/collection'), + fs = require('fs'), + path = require('path'), + { + parseBody + } = require('../../lib/util/parseBody'), + collectionsPath = './fixtures'; + +describe('parseBody function', function () { + + it('should parse a raw json Body', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[8].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'body = \'{\n "json": "Test-Test"\n}\';\n\n'; + let bodySnippet = parseBody(body, indentation, bodyTrim, 'application/json'); + expect(bodySnippet).to.equal(expectedBody); + }); + + it('should parse a raw json Body with indentation', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[8].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'body = \'{\n "json": "Test-Test"\n}\';\n\n'; + let bodySnippet = parseBody(body, indentation, bodyTrim, 'application/json'); + expect(bodySnippet).to.equal(expectedBody); + }); + + it('should parse a raw xml Body', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[10].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'body = "\n\tTest Test\n"\n\n'; + let bodySnippet = parseBody(body, indentation, bodyTrim, 'application/json'); + expect(bodySnippet).to.equal(expectedBody); + }); + + it('should return form-url-encoded params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[7].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'body = list(\n' + + ' \'\\\'3\\\'\' = \'c\',\n' + + ' \'"4" \' = \'d \',\n' + + ' \'Special\' = \'!@#$%&*()^_=`~\',\n' + + ' \'more\' = \',./\\\';[]}{":?><|\\\\\\\\\'\n' + + ')\n\n', + result = parseBody(body, indentation, bodyTrim, 'application/x-www-form-urlencoded'); + expect(result).to.equal(expectedBody); + + }); + + it('should return form-data params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[4].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'body = list(\n' + + ' \'pl\' = \'\\\'a\\\'\',\n' + + ' \'qu\' = \'"b"\',\n' + + ' \'hdjkljh \' = \'c \',\n' + + ' \'sa\' = \'d\',\n' + + ' \'Special \' = \'!@#$%&*()^_+=`~ \',\n' + + ' \'more\' = \',./\\\';[]}{":?><|\\\\\\\\\'\n' + + ')\n\n', + result = parseBody(body, indentation, bodyTrim, 'formdata'); + expect(result).to.equal(expectedBody); + + }); + + it('should return form-data params with a file', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[26].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'body = list(\n' + + ' \'test-file\' = upload_file(\'/path/to/file\')\n' + + ')\n\n', + result = parseBody(body, indentation, bodyTrim, 'formdata'); + expect(result).to.equal(expectedBody); + + }); + + it('should return binary data params with a file', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[25].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'body = upload_file(\'\')\n\n', + result = parseBody(body, indentation, bodyTrim, 'formdata'); + expect(result).to.equal(expectedBody); + + }); + + it('should return graphql params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[27].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'body = \'{"query":"{\\\\n findScenes(\\\\n filter: {per_page: 0}\\\\n ' + + ' scene_filter: {is_missing: \\\\"performers\\\\"}){\\\\n count\\\\n scenes' + + ' {\\\\n id\\\\n title\\\\n path\\\\n }\\\\n }\\\\n}","variables":' + + '{"variable_key":"variable_value"}}\'\n\n', + result = parseBody(body, indentation, bodyTrim, 'graphql'); + expect(result).to.equal(expectedBody); + + }); + + it('should return empty form data', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[28].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = '', + result = parseBody(body, indentation, bodyTrim, 'graphql'); + expect(result).to.equal(expectedBody); + + }); +}); diff --git a/codegens/r-httr/test/unit/rHttr.test.js b/codegens/r-httr/test/unit/rHttr.test.js new file mode 100644 index 000000000..fc6ee67ee --- /dev/null +++ b/codegens/r-httr/test/unit/rHttr.test.js @@ -0,0 +1,146 @@ +var expect = require('chai').expect, + { + getSnippetHeaders, + getSnippetFromMethod, + getSnippetRequest, + getIndentation + } = require('../../lib/rHttr'); + +describe('getSnippetHeaders function', function () { + + it('should generate headers declaration snippet', function () { + const expected = 'headers = c(\n \'"1"\' = \'\\\'a\\\'\',\n \'"2"\' = \'"b"\'\n)\n\n', + res = getSnippetHeaders([{ key: '"1"', value: '\'a\''}, { key: '"2"', value: '"b"'}], ' '); + expect(res).to.equal(expected); + }); + + it('should generate empty headers declaration snippet without headers', function () { + const expected = '', + res = getSnippetHeaders([ ], ' '); + expect(res).to.equal(expected); + }); + + it('should generate headers declaration snippet with empty indentation', function () { + const expected = 'headers = c(\n\'"1"\' = \'\\\'a\\\'\',\n\'"2"\' = \'"b"\'\n)\n\n', + res = getSnippetHeaders([{ key: '"1"', value: '\'a\''}, { key: '"2"', value: '"b"'}], ''); + expect(res).to.equal(expected); + }); + +}); + +describe('getSnippetFromMethod function', function () { + + it('should generate postForm snippet with params headers and post style', function () { + const expected = 'res <- VERB("POST", url = "https://postman-echo.com/post", ' + + 'body = body, add_headers(headers), encode = \'form\')\n\n', + res = getSnippetFromMethod('https://postman-echo.com/post', true, true, 'POST', 'urlencoded'); + expect(res).to.equal(expected); + }); + + it('should generate postForm snippet without params with headers and post style', function () { + const expected = 'res <- VERB("POST", url = "https://postman-echo.com/post", ' + + 'add_headers(headers), encode = \'form\')\n\n', + res = getSnippetFromMethod( + 'https://postman-echo.com/post', + false, + true, + 'POST', + 'urlencoded' + ); + expect(res).to.equal(expected); + }); + + it('should generate postForm snippet without params with headers and post style', function () { + const expected = 'res <- VERB("POST", url = "https://postman-echo.com/post", ' + + 'add_headers(headers), encode = \'form\', timeout(3))\n\n', + res = getSnippetFromMethod( + 'https://postman-echo.com/post', + false, + true, + 'POST', + 'urlencoded', + 3 + ); + expect(res).to.equal(expected); + }); + + it('should generate GET snippet with params headers', function () { + const expected = 'res <- VERB("GET", url = "https://postman-echo.com/headers", ' + + 'add_headers(headers))\n\n', + res = getSnippetFromMethod('https://postman-echo.com/headers', false, true, 'GET', undefined, undefined); + expect(res).to.equal(expected); + }); + + it('should generate GET snippet without headers', function () { + const expected = 'res <- VERB("GET", url = "https://postman-echo.com/headers")\n\n', + res = getSnippetFromMethod('https://postman-echo.com/headers', false, false, 'GET', undefined, undefined); + expect(res).to.equal(expected); + }); + + it('should generate GET snippet with timeout', function () { + const expected = 'res <- VERB("GET", url = "https://postman-echo.com/headers", timeout(3))\n\n', + res = getSnippetFromMethod('https://postman-echo.com/headers', false, false, 'GET', undefined, 3); + expect(res).to.equal(expected); + }); + +}); + +describe('getSnippetRequest function', function () { + + it('should generate snippet method GET with headers', function () { + const expected = 'res <- VERB("GET", url = "https://postman-echo.com/headers", add_headers(headers))\n\n', + res = getSnippetRequest({ + url: 'https://postman-echo.com/headers', + method: 'GET', + hasParams: false, + hasHeaders: true + }); + expect(res).to.equal(expected); + }); + + it('should generate snippet method GET without headers', function () { + const expected = 'res <- VERB("GET", url = "https://postman-echo.com/headers")\n\n', + res = getSnippetRequest({ + url: 'https://postman-echo.com/headers', + method: 'GET', + hasParams: false, + hasHeaders: false + }); + expect(res).to.equal(expected); + }); + + it('should generate snippet method GET without headers and timeout', function () { + const expected = 'res <- VERB("GET", url = "https://postman-echo.com/headers", timeout(3))\n\n', + res = getSnippetRequest({ + url: 'https://postman-echo.com/headers', + method: 'GET', + hasParams: false, + hasHeaders: false, + requestTimeout: 3 + }); + expect(res).to.equal(expected); + }); + + it('should generate snippet method HEAD', function () { + const expected = 'res <- VERB("HEAD", url = "https://postman-echo.com/headers")\n\n', + res = getSnippetRequest({ + url: 'https://postman-echo.com/headers', + method: 'HEAD', + hasParams: false, + hasHeaders: false + }); + expect(res).to.equal(expected); + }); +}); + +describe('getIndentation method', function () { + it('should return two spaces', function () { + const options = { + indentType: 'Space', + indentCount: 2 + }, + expected = ' ', + result = getIndentation(options); + expect(result).to.be.equal(expected); + }); +}); diff --git a/codegens/r-rcurl/.gitignore b/codegens/r-rcurl/.gitignore new file mode 100644 index 000000000..ae0667e09 --- /dev/null +++ b/codegens/r-rcurl/.gitignore @@ -0,0 +1,49 @@ +.DS_Store +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + +# Coverage directory used by tools like istanbul +.coverage + +# node-waf configuration +.lock-wscript + + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ + +test/unit/fixtures/snippet.r diff --git a/codegens/r-rcurl/.npmignore b/codegens/r-rcurl/.npmignore new file mode 100644 index 000000000..79ad2ba5f --- /dev/null +++ b/codegens/r-rcurl/.npmignore @@ -0,0 +1,76 @@ +### NPM Specific: Disregard recursive project files +### =============================================== +/.editorconfig +/.gitmodules +/test + +### Borrowed from .gitignore +### ======================== + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +snippet.swift + +out/ diff --git a/codegens/r-rcurl/README.md b/codegens/r-rcurl/README.md new file mode 100644 index 000000000..c7d58fe12 --- /dev/null +++ b/codegens/r-rcurl/README.md @@ -0,0 +1,44 @@ + +> Converts Postman-SDK Request into code snippet for R - Rcurl. + +#### Prerequisites +To run Code-Gen, ensure that you have NodeJS >= v8. A copy of the NodeJS installable can be downloaded from https://nodejs.org/en/download/package-manager. + +## Using the Module +The module will expose an object which will have property `convert` which is the function for converting the Postman-SDK request to R-RCurl code snippet. + +### convert function +Convert function takes three parameters + +* `request` - Postman-SDK Request Object + +* `options` - options is an object which hsa following properties + * `indentType` - String denoting type of indentation for code snippet. eg: 'Space', 'Tab' + * `indentCount` - The number of indentation characters to add per code level + * `trimRequestBody` - Whether or not request body fields should be trimmed + * `followRedirect` : Boolean denoting whether to redirect a request (default: true) + * `ignoreWarnings` : Add code to ignore R warnings (default: false) + +* `callback` - callback function with first parameter as error and second parameter as string for code snippet + +##### Example: +```js +var request = new sdk.Request('www.google.com'), //using postman sdk to create request + options = { + indentCount: 3, + indentType: 'Space', + requestTimeout: 200, + trimRequestBody: true + }; +convert(request, options, function(error, snippet) { + if (error) { + // handle error + } + // handle snippet +}); +``` +### Guidelines for using generated snippet + +* Since Postman-SDK Request object doesn't provide complete path of the file, it needs to be manually inserted in case of uploading a file. + +* This module doesn't support cookies. diff --git a/codegens/r-rcurl/index.js b/codegens/r-rcurl/index.js new file mode 100644 index 000000000..bb0a047c4 --- /dev/null +++ b/codegens/r-rcurl/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/codegens/r-rcurl/lib/index.js b/codegens/r-rcurl/lib/index.js new file mode 100644 index 000000000..d05156a98 --- /dev/null +++ b/codegens/r-rcurl/lib/index.js @@ -0,0 +1,4 @@ +module.exports = { + convert: require('./rRcurl').convert, + getOptions: require('./rRcurl').getOptions +}; diff --git a/codegens/r-rcurl/lib/lodash.js b/codegens/r-rcurl/lib/lodash.js new file mode 100644 index 000000000..5be147afd --- /dev/null +++ b/codegens/r-rcurl/lib/lodash.js @@ -0,0 +1,455 @@ +/* istanbul ignore next */ +module.exports = { + + /** + * Checks if `value` is an empty object, array or string. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Values such as strings, arrays are considered empty if they have a `length` of `0`. + * + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * isEmpty(null) + * // => true + * + * isEmpty(true) + * // => true + * + * isEmpty(1) + * // => true + * + * isEmpty([1, 2, 3]) + * // => false + * + * isEmpty('abc') + * // => false + * + * isEmpty({ 'a': 1 }) + * // => false + */ + isEmpty: function (value) { + // eslint-disable-next-line lodash/prefer-is-nil + if (value === null || value === undefined) { + return true; + } + if (Array.isArray(value) || typeof value === 'string' || typeof value.splice === 'function') { + return !value.length; + } + + for (const key in value) { + if (Object.prototype.hasOwnProperty.call(value, key)) { + return false; + } + } + return true; + }, + + /** + * Checks if `value` is `undefined`. + * + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * isUndefined(void 0) + * // => true + * + * isUndefined(null) + * // => false + */ + isUndefined: function (value) { + return value === undefined; + }, + + /** + * Checks if `func` is classified as a `Function` object. + * + * @param {*} func The value to check. + * @returns {boolean} Returns `true` if `func` is a function, else `false`. + * @example + * + * isFunction(self.isEmpty) + * // => true + * + * isFunction(/abc/) + * // => false + */ + isFunction: function (func) { + return typeof func === 'function'; + }, + + /** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * capitalize('FRED') + * // => 'Fred' + * + * capitalize('john') + * // => 'John' + */ + + capitalize: function (string) { + return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); + }, + + /** + * Reduces `array` to a value which is the accumulated result of running + * each element in `array` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `array` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, array). + * + * @param {Array} array The Array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @example + * + * reduce([1, 2], (sum, n) => sum + n, 0) + * // => 3 + * + */ + reduce: function (array, iteratee, accumulator) { + return array.reduce(iteratee, accumulator); + }, + + /** + * Iterates over elements of `array`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function|object} predicate The function/object invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * filter(users, ({ active }) => active) + * // => object for ['barney'] + */ + filter: function (array, predicate) { + if (typeof predicate === 'function') { + return array.filter(predicate); + } + var key = Object.keys(predicate), + val = predicate[key], + res = []; + array.forEach(function (item) { + if (item[key] && item[key] === val) { + res.push(item); + } + }); + return res; + }, + + /** + * The opposite of `filter` this method returns the elements of `array` + * that `predicate` does **not** return truthy for. + * + * @param {Array} array collection to iterate over. + * @param {String} predicate The String that needs to have truthy value, invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * reject(users, 'active') + * // => object for ['fred'] + */ + reject: function (array, predicate) { + var res = []; + array.forEach((object) => { + if (!object[predicate]) { + res.push(object); + } + }); + return res; + }, + + /** + * Creates an array of values by running each element of `array` thru `iteratee`. + * The iteratee is invoked with three arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n + * } + * + * map([4, 8], square) + * // => [16, 64] + */ + map: function (array, iteratee) { + return array.map(iteratee); + }, + + /** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @example + * + * forEach([1, 2], value => console.log(value)) + * // => Logs `1` then `2`. + * + * forEach({ 'a': 1, 'b': 2 }, (value, key) => console.log(key)) + * // => Logs 'a' then 'b' + */ + + forEach: function (collection, iteratee) { + if (collection === null) { + return null; + } + + if (Array.isArray(collection)) { + return collection.forEach(iteratee); + } + const iterable = Object(collection), + props = Object.keys(collection); + var index = -1, + key, i; + + for (i = 0; i < props.length; i++) { + key = props[++index]; + iteratee(iterable[key], key, iterable); + } + return collection; + }, + + /** + * Checks if `value` is in `collection`. If `collection` is a string, it's + * checked for a substring of `value`, otherwise it checks if the `value` is present + * as a key in a `collection` object. + * + * @param {Array|Object|string} collection The collection to inspect. + * @param {*} value The value to search for. + * @returns {boolean} Returns `true` if `value` is found, else `false`. + * @example + * + * _.includes([1, 2, 3], 1); + * // => true + * + * _.includes({ 'a': 1, 'b': 2 }, 1); + * // => true + * + * _.includes('abcd', 'bc'); + * // => true + */ + includes: function (collection, value) { + if (Array.isArray(collection) || typeof collection === 'string') { + return collection.includes(value); + } + for (var key in collection) { + if (collection.hasOwnProperty(key)) { + if (collection[key] === value) { + return true; + } + } + } + return false; + }, + + /** + * Gets the size of `collection` by returning its length for array and strings. + * For objects it returns the number of enumerable string keyed + * properties. + * + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * size([1, 2, 3]) + * // => 3 + * + * size({ 'a': 1, 'b': 2 }) + * // => 2 + * + * size('pebbles') + * // => 7 + */ + size: function (collection) { + // eslint-disable-next-line lodash/prefer-is-nil + if (collection === null || collection === undefined) { + return 0; + } + if (Array.isArray(collection) || typeof collection === 'string') { + return collection.length; + } + + return Object.keys(collection).length; + }, + + /** + * Converts all elements in `array` into a string separated by `separator`. + * + * @param {Array} array The array to convert. + * @param {string} [separator=','] The element separator. + * @returns {string} Returns the joined string. + * @example + * + * _.join(['a', 'b', 'c'], '~'); + * // => 'a~b~c' + */ + join: function (array, separator) { + if (array === null) { + return ''; + } + return array.join(separator); + }, + + /** + * Removes trailing whitespace or specified characters from `string`. + * + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + * @example + * + * trimEnd(' abc ') + * // => ' abc' + * + * trimEnd('-_-abc-_-', '_-') + * // => '-_-abc' + */ + trimEnd: function (string, chars) { + if (!string) { + return ''; + } + if (string && !chars) { + return string.replace(/\s*$/, ''); + } + chars += '$'; + return string.replace(new RegExp(chars, 'g'), ''); + }, + + /** + * Returns the index of the first + * element `predicate` returns truthy for. + * + * @param {Array} array The array to inspect. + * @param {Object} predicate The exact object to be searched for in the array. + * @returns {number} Returns the index of the found element, else `-1`. + * @example + * + * var users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': true } + * ]; + * + * _.findIndex(users, { 'user': 'fred', 'active': false }); + * // => 1 + * + * _.findIndex(users, {'active' : false}); + * // => 0 + * + */ + findIndex: function (array, predicate) { + var length = array === null ? 0 : array.length, + index = -1, + keys = Object.keys(predicate), + found, i; + if (!length) { + return -1; + } + for (i = 0; i < array.length; i++) { + found = true; + // eslint-disable-next-line no-loop-func + keys.forEach((key) => { + if (!(array[i][key] && array[i][key] === predicate[key])) { + found = false; + } + }); + if (found) { + index = i; + break; + } + } + return index; + }, + + /** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @param {Object} object The object to query. + * @param {string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * const object = { a: {b : 'c'} } + * + * + * get(object, 'a.b.c', 'default') + * // => 'default' + * + * get(object, 'a.b', 'default') + * // => 'c' + */ + get: function (object, path, defaultValue) { + if (object === null) { + return undefined; + } + var arr = path.split('.'), + res = object, + i; + for (i = 0; i < arr.length; i++) { + res = res[arr[i]]; + if (res === undefined) { + return defaultValue; + } + } + return res; + }, + + /** + * Checks if `predicate` returns truthy for **all** elements of `array`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * every([true, 1, null, 'yes'], Boolean) + * // => false + */ + every: function (array, predicate) { + var index = -1, + length = array === null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; + } + +}; diff --git a/codegens/r-rcurl/lib/options.js b/codegens/r-rcurl/lib/options.js new file mode 100644 index 000000000..e9476c695 --- /dev/null +++ b/codegens/r-rcurl/lib/options.js @@ -0,0 +1,71 @@ +const options = [ + { + name: 'Set indentation count', + id: 'indentCount', + type: 'positiveInteger', + default: 2, + description: + 'Set the number of indentation characters to add per code level' + }, + { + name: 'Set indentation type', + id: 'indentType', + type: 'enum', + availableOptions: ['Tab', 'Space'], + default: 'Space', + description: 'Select the character used to indent lines of code' + }, + { + name: 'Trim request body fields', + id: 'trimRequestBody', + type: 'boolean', + default: false, + description: + 'Remove white space and additional lines that may affect the server\'s response' + }, + { + name: 'Set request timeout', + id: 'requestTimeout', + type: 'positiveInteger', + default: 0, + description: + 'Set number of milliseconds the request should wait for a response' + + ' before timing out (use 0 for infinity)' + }, + { + name: 'Follow redirects', + id: 'followRedirect', + type: 'boolean', + default: true, + description: 'Automatically follow HTTP redirects' + }, + { + name: 'Ignore warnings', + id: 'ignoreWarnings', + type: 'boolean', + default: false, + description: 'Ignore warnings from R' + } +]; + +/** + * Used in order to get options for generation of R-RCurl code snippet + * + * @module getOptions + * + * @returns {Array} Options specific to generation of R-RCurl code snippet + */ +function getOptions () { + return options; +} + +module.exports = { + /** + * Used in order to get options for generation of R-RCurl code snippet + * + * @module getOptions + * + * @returns {Array} Options specific to generation of R-RCurl code snippet + */ + getOptions +}; diff --git a/codegens/r-rcurl/lib/rRcurl.js b/codegens/r-rcurl/lib/rRcurl.js new file mode 100644 index 000000000..89783f47b --- /dev/null +++ b/codegens/r-rcurl/lib/rRcurl.js @@ -0,0 +1,501 @@ +const getOptions = require('./options').getOptions, + sanitizeString = require('./util/sanitize').sanitizeString, + sanitizeOptions = require('./util/sanitize').sanitizeOptions, + parseBody = require('./util/parseBody').parseBody; + +/** + * Takes in an array and group the ones with same key + * + * @param {Array} headerArray - postman SDK-headers + * @returns {String} - request headers in the desired format + */ +function groupHeadersSameKey (headerArray) { + let res = [], + group = headerArray.reduce((header, a) => { + header[a.key] = [...header[a.key] || [], a]; + return header; + }, {}); + Object.keys(group).forEach((item) => { + let values = []; + group[item].forEach((child) => { + values.push(child.value); + }); + res.push({key: item, value: values.join(', ') }); + }); + return res; +} + +/** + * Returns the snippet header + * + * @module convert + * @param {boolean} ignoreWarnings - option to add code for ignoring warnings + * @returns {string} the snippet headers (uses) + */ +function getSnippetHeader (ignoreWarnings) { + if (ignoreWarnings) { + return 'library(RCurl)\noptions(warn=-1)\n'; + } + return 'library(RCurl)\n'; + +} + +/** + * Returns the snippet footer + * + * @module convert + * @returns {string} the snippet headers (uses) + */ +function getSnippetFooter () { + return 'cat(res)'; +} + +/** + * Gets the defined indentation from options + * + * @param {object} options - process options + * @returns {String} - indentation characters + */ +function getIndentation (options) { + if (options && options.indentType && options.indentCount) { + let charIndentation = options.indentType === 'Tab' ? '\t' : ' '; + return charIndentation.repeat(options.indentCount); + } + return ' '; +} + +/** + * Used to get the headers and put them in the desired form of the language + * + * @param {Object} request - postman SDK-request object + * @returns {String} - request headers in the desired format + */ +function getRequestHeaders (request) { + return request.headers.members; +} + +/** + * Returns the request's url in string format + * + * @param {Object} request - postman SDK-request object + * @returns {String} - request url in string representation + */ +function getRequestURL (request) { + return request.url.toString(); +} + +/** + * Validates if the input is a function + * + * @module convert + * + * @param {*} validateFunction - postman SDK-request object + * @returns {boolean} true if is a function otherwise false + */ +function validateIsFunction (validateFunction) { + return typeof validateFunction === 'function'; +} + +/** + * Returns the request's url in string format + * + * @param {Object} request - postman SDK-request object + * @returns {String} - request url in string representation + */ +function getRequestMethod (request) { + return request.method; +} + +/** + * Transforms an array of headers into the desired form of the language + * + * @param {Array} mapToSnippetArray - array of key values + * @param {String} indentation - used for indenting snippet's structure + * @param {boolean} sanitize - whether to sanitize the key and values + * @returns {String} - array in the form of [ key => value ] + */ +function getSnippetArray (mapToSnippetArray, indentation, sanitize) { + mapToSnippetArray = groupHeadersSameKey(mapToSnippetArray); + let mappedArray = mapToSnippetArray.map((entry) => { + return `${indentation}"${sanitize ? sanitizeString(entry.key, true) : entry.key}" = ` + + `${sanitize ? '"' + sanitizeString(entry.value) + '"' : entry.value}`; + }); + return `c(\n${mappedArray.join(',\n')}\n)`; +} + +/** + * Transforms an array of headers into the desired form of the language + * + * @param {Array} headers - postman SDK-headers + * @param {String} indentation - used for indenting snippet's structure + * @returns {String} - request headers in the desired format + */ +function getSnippetHeaders (headers, indentation) { + if (headers.length > 0) { + headers = headers.filter((header) => { return !header.disabled; }); + return `headers = ${getSnippetArray(headers, indentation, true)}\n`; + } + return ''; +} + +/** + * Creates the snippet request for the request options + * + * @module convert + * + * @param {boolean} hasParams - wheter or not include the params + * @param {boolean} hasHeaders - wheter or not include the headers + * @param {number} requestTimeout - the request timeout + * @param {boolean} followRedirect - wheter to follow location or not + * @returns {String} - returns generated snippet +*/ +function buildOptionsSnippet (hasParams, hasHeaders, requestTimeout, followRedirect) { + let options = [], + mappedArray; + if (hasParams) { + options.push({ key: 'postfields', value: 'params' }); + } + if (hasHeaders) { + options.push({ key: 'httpheader', value: 'headers' }); + } + if (requestTimeout && requestTimeout !== 0) { + options.push({ key: 'timeout.ms', value: requestTimeout }); + } + if (followRedirect === true) { + options.push({ key: 'followlocation', value: 'TRUE' }); + } + mappedArray = options.map((entry) => { + return `${entry.key} = ${entry.value}`; + }); + return `${mappedArray.join(', ')}`; +} + +/** + * Creates the snippet request for the postForm method + * + * @module convert + * + * @param {object} filesInfo - information of the form data files + * @returns {String} - returns generated snippet + */ +function buildFileRequestSnippet (filesInfo) { + if (!filesInfo || filesInfo.numberOfFiles === 0) { + return ''; + } + let files = []; + for (let index = 0; index < filesInfo.numberOfFiles; index++) { + files.push(`file = file${index}`); + } + return `${files.join(', ')}, `; +} + + +/** + * Creates the snippet request for the postForm method + * + * @module convert + * + * @param {string} url - string url of the service + * @param {string} style - "post":urlencoded params "httpost":multipart/form-data + * @param {boolean} hasParams - wheter or not include the params + * @param {boolean} hasHeaders - wheter or not include the header + * @param {number} requestTimeout - the request timeout + * @param {boolean} followRedirect - follow redirect from options + * @param {object} filesInfo - information of the form data files + * @returns {String} - returns generated snippet + */ +function getSnippetPostFormInParams (url, style, hasParams, hasHeaders, requestTimeout, followRedirect, + filesInfo) { + let optionsSnipppet = buildOptionsSnippet(false, hasHeaders, requestTimeout, followRedirect), + fileRequestSnippet = buildFileRequestSnippet(filesInfo), + paramsSnippet = hasParams ? '.params = params, ' : ''; + if (optionsSnipppet !== '') { + return `res <- postForm("${url}", ${fileRequestSnippet}${paramsSnippet}.opts=list(${optionsSnipppet}),` + + ` style = "${style}")\n`; + } + return `res <- postForm("${url}", ${fileRequestSnippet}${paramsSnippet}style = "${style}")\n`; +} + +/** + * Creates the snippet request for the getUrl method + * + * @module convert + * + * @param {string} url - string url of the service + * @param {string} hasHeaders - wheter or not include the headers + * @param {number} requestTimeout - the request timeout + * @param {boolean} followRedirect - follow redirect from options + * @returns {String} - returns generated snippet + */ +function getSnippetGetURL (url, hasHeaders, requestTimeout, followRedirect) { + let optionsSnipppet = buildOptionsSnippet(false, hasHeaders, requestTimeout, followRedirect); + + if (optionsSnipppet !== '') { + return `res <- getURL("${url}", .opts=list(${optionsSnipppet}))\n`; + } + return `res <- getURL("${url}")\n`; +} + +/** + * Creates the snippet request for the postForm method + * + * @module convert + * + * @param {string} url - string url of the service + * @param {string} style - "post":urlencoded params "httpost":multipart/form-data + * @param {boolean} hasParams - wheter or not include the params + * @param {boolean} hasHeaders - wheter or not include the headers + * @param {number} requestTimeout - the request timeout + * @param {boolean} followRedirect - follow redirect from options + * @returns {String} - returns generated snippet + */ +function getSnippetPostFormInOptions (url, style, hasParams, hasHeaders, requestTimeout, followRedirect) { + let optionsSnipppet = buildOptionsSnippet(hasParams, hasHeaders, requestTimeout, followRedirect); + if (optionsSnipppet !== '') { + return `res <- postForm("${url}", .opts=list(${optionsSnipppet}),` + + ` style = "${style}")\n`; + } + return `res <- postForm("${url}", style = "${style}")\n`; +} + +/** + * Creates the snippet request for the httpPut method + * + * @module convert + * + * @param {string} url - string url of the service + * @param {boolean} hasParams - wheter or not include the params + * @param {boolean} hasHeaders - wheter or not include the headers + * @param {number} requestTimeout - the request timeout + * @param {boolean} followRedirect - follow redirect from options + * @returns {String} - returns generated snippet + */ +function getSnippetPut (url, hasParams, hasHeaders, requestTimeout, followRedirect) { + let optionsSnipppet = buildOptionsSnippet(false, hasHeaders, requestTimeout, followRedirect); + if (optionsSnipppet !== '' && hasParams) { + return `res <- httpPUT("${url}", params, ${optionsSnipppet})\n`; + } + else if (optionsSnipppet !== '' && !hasParams) { + return `res <- httpPUT("${url}", ${optionsSnipppet})\n`; + } + else if (optionsSnipppet === '' && hasParams) { + return `res <- httpPUT("${url}", params)\n`; + } + else if (optionsSnipppet === '' && !hasParams) { + return `res <- httpPUT("${url}")\n`; + } + return ''; +} + +/** + * Creates the snippet request for the httpPut method + * + * @module convert + * + * @param {string} url - string url of the service + * @param {boolean} hasParams - wheter or not include the params + * @param {boolean} hasHeaders - wheter or not include the headers + * @param {number} requestTimeout - the request timeout + * @param {boolean} followRedirect - follow redirect from options + * @returns {String} - returns generated snippet + */ +function getSnippetDelete (url, hasParams, hasHeaders, requestTimeout, followRedirect) { + let optionsSnipppet = buildOptionsSnippet(hasParams, hasHeaders, requestTimeout, followRedirect); + if (optionsSnipppet !== '') { + return `res <- httpDELETE("${url}", ${optionsSnipppet})\n`; + } + return `res <- httpDELETE("${url}")\n`; +} + +/** + * Creates the snippet request with get rul content for other verbs than + * POST, PUT, DELETE, and GET + * + * @module convert + * + * @param {string} url - string url of the service + * @param {boolean} hasParams - wheter or not include the params + * @param {boolean} hasHeaders - wheter or not include the headers + * @param {number} requestTimeout - the request timeout + * @param {boolean} followRedirect - follow redirect from options + * @param {string} httpMethod - http method of the request + * @returns {String} - returns generated snippet + */ +function getSnippetURLContent (url, hasParams, hasHeaders, requestTimeout, followRedirect, httpMethod) { + let optionsSnipppet = buildOptionsSnippet(hasParams, hasHeaders, requestTimeout, followRedirect); + if (optionsSnipppet !== '') { + return `res <- getURLContent("${url}", customrequest = "${httpMethod}", ${optionsSnipppet})\n`; + } + return `res <- getURLContent("${url}", customrequest = "${httpMethod}")\n`; +} + +/** + * Creates the snippet request for either get ulr or post form + * + * @module convert + * + * @param {string} url - string url of the service + * @param {string} method - request http method + * @param {string} style - "post":urlencoded params "httpost":multipart/form-data + * @param {boolean} hasParams - wheter or not include the params + * @param {boolean} hasHeaders - wheter or not include the headers + * @param {string} contentTypeHeaderValue - the content type header value + * @param {object} request - the PM request + * @param {number} requestTimeout - request timeout from options + * @param {boolean} followRedirect - follow redirect from options + * @param {object} filesInfo - information of the form data files + * @returns {String} - returns generated snippet + */ +function getSnippetRequest (url, method, style, hasParams, hasHeaders, contentTypeHeaderValue, + request, requestTimeout, followRedirect, filesInfo) { + const methodUC = method.toUpperCase(); + if (methodUC === 'GET') { + return getSnippetGetURL(url, hasHeaders, requestTimeout, followRedirect); + } + if (methodUC === 'POST' && request.body && request.body.mode === 'file') { + return getSnippetPostFormInOptions(url, 'post', hasParams, hasHeaders, requestTimeout, followRedirect); + } + if (methodUC === 'POST' && contentTypeHeaderValue === 'application/x-www-form-urlencoded' || + contentTypeHeaderValue === 'multipart/form-data' || filesInfo !== undefined) { + return getSnippetPostFormInParams(url, style, hasParams, hasHeaders, requestTimeout, followRedirect, + filesInfo); + } + if (methodUC === 'POST') { + return getSnippetPostFormInOptions(url, style, hasParams, hasHeaders, requestTimeout, followRedirect); + } + if (methodUC === 'PUT') { + return getSnippetPut(url, hasParams, hasHeaders, requestTimeout, followRedirect); + } + if (methodUC === 'DELETE') { + return getSnippetDelete(url, hasParams, hasHeaders, requestTimeout, followRedirect); + } + return getSnippetURLContent(url, hasParams, hasHeaders, requestTimeout, followRedirect, methodUC); +} + +/** + * Gets the defined body trim from options + * + * @param {object} options - process options + * @returns {boolea} - wheter to trim the request body + */ +function getBodyTrim (options) { + if (options && options.trimRequestBody) { + return options.trimRequestBody; + } + return false; +} + +/** + * Gets the http post style + * + *"post":urlencoded params "httpost":multipart/form-data + + * @param {string} method - request http method + * @param {string} contentType - request content type + * @returns {string} - the post form style + */ +function getCurlStyle (method, contentType) { + if (method.toUpperCase() === 'POST') { + if (contentType === 'application/x-www-form-urlencoded') { + return 'post'; + } + return 'httppost'; + } + return ''; +} + +/** + * Add the content type header if needed + * + * @module convert + * + * @param {Object} request - postman SDK-request object + */ +function addContentTypeHeader (request) { + if (request.body && request.body.mode === 'graphql' && !request.headers.has('Content-Type')) { + request.addHeader({ + key: 'Content-Type', + value: 'application/json' + }); + } +} + + +/** + * Used to convert the postman sdk-request object in PHP-Guzzle request snippet + * + * @module convert + * + * @param {Object} request - postman SDK-request object + * @param {object} options - process options + * @param {Function} callback - function with parameters (error, snippet) + * @returns {String} - returns generated PHP-Guzzle snippet via callback + */ +function convert (request, options, callback) { + + if (!validateIsFunction(callback)) { + throw new Error('R-Rcurl~convert: Callback is not a function'); + } + let snippet = '', + snippetRequest, + snippetBody; + options = sanitizeOptions(options, getOptions()); + addContentTypeHeader(request); + const method = getRequestMethod(request), + indentation = getIndentation(options), + connectionTimeout = options.requestTimeout, + followRedirect = options.followRedirect, + ignoreWarnings = options.ignoreWarnings, + contentTypeHeaderValue = request.headers.get('Content-Type'), + url = sanitizeString(getRequestURL(request)), + snippetHeaders = getSnippetHeaders(getRequestHeaders(request), indentation), + snippetHeader = getSnippetHeader(ignoreWarnings), + snippetFooter = getSnippetFooter(); + snippetBody = parseBody(request.body, indentation, getBodyTrim(options), contentTypeHeaderValue); + if (typeof snippetBody === 'string') { + snippetRequest = getSnippetRequest(url, method, getCurlStyle(method, contentTypeHeaderValue), + snippetBody !== '', snippetHeaders !== '', contentTypeHeaderValue, request, connectionTimeout, followRedirect); + } + else { + let paramsBody = snippetBody.bodySnippet, + filesInfo = { fileSnippet: snippetBody.fileSnippet, + numberOfFiles: snippetBody.numberOfFiles}; + snippetRequest = getSnippetRequest(url, method, getCurlStyle(method, contentTypeHeaderValue), + paramsBody !== '', snippetHeaders !== '', contentTypeHeaderValue, request, connectionTimeout, followRedirect, + filesInfo); + snippetBody = paramsBody + filesInfo.fileSnippet; + } + + snippet += snippetHeader; + snippet += snippetHeaders; + snippet += snippetBody; + snippet += snippetRequest; + snippet += snippetFooter; + + return callback(null, snippet); +} + +module.exports = { + /** + * Used in order to get options for generation of R-rCurl code snippet + * + * @module getOptions + * + * @returns {Array} Options specific to generation of R-rCurl code snippet + */ + getOptions, + + convert, + getSnippetHeaders, + getSnippetPostFormInParams, + getSnippetGetURL, + getSnippetRequest, + getSnippetPostFormInOptions, + addContentTypeHeader, + buildOptionsSnippet, + groupHeadersSameKey, + getIndentation, + getSnippetPut, + getSnippetDelete, + getSnippetURLContent +}; diff --git a/codegens/r-rcurl/lib/util/parseBody.js b/codegens/r-rcurl/lib/util/parseBody.js new file mode 100644 index 000000000..40cebebb8 --- /dev/null +++ b/codegens/r-rcurl/lib/util/parseBody.js @@ -0,0 +1,293 @@ +const sanitizeString = require('./sanitize').sanitizeString, + _ = require('../lodash'); + +/** + * + * @param {Array} array - form data array + * @param {String} key - key of form data param + * @param {String} type - type of form data param(file/text) + * @param {String} val - value/src property of form data param + * @param {String} disabled - Boolean denoting whether the param is disabled or not + * @param {String} contentType - content type header of the param + * + * Appends a single param to form data array + */ +function addFormParam (array, key, type, val, disabled, contentType) { + if (type === 'file') { + array.push({ + key: key, + type: type, + src: val, + disabled: disabled, + contentType: contentType + }); + } + else { + array.push({ + key: key, + type: type, + value: val, + disabled: disabled, + contentType: contentType + }); + } +} + +/** +* parses a body to the corresponding snippet +* +* @param {object} body - postman request body +* @returns {String} +*/ +function solveMultiFile (body) { + if (body && body.mode === 'formdata') { + let formdata = body.formdata, + formdataArray = []; + formdata.members.forEach((param) => { + let key = param.key, + type = param.type, + disabled = param.disabled, + contentType = param.contentType; + // check if type is file or text + if (type === 'file') { + // if src is not of type string we check for array(multiple files) + if (typeof param.src !== 'string') { + // if src is an array(not empty), iterate over it and add files as separate form fields + if (Array.isArray(param.src) && param.src.length) { + param.src.forEach((filePath) => { + addFormParam(formdataArray, key, param.type, filePath, disabled, contentType); + }); + } + // if src is not an array or string, or is an empty array, add a placeholder for file path(no files case) + else { + addFormParam(formdataArray, key, param.type, '/path/to/file', disabled, contentType); + } + } + // if src is string, directly add the param with src as filepath + else { + addFormParam(formdataArray, key, param.type, param.src, disabled, contentType); + } + } + // if type is text, directly add it to formdata array + else { + addFormParam(formdataArray, key, param.type, param.value, disabled, contentType); + } + }); + + body.update({ + mode: 'formdata', + formdata: formdataArray + }); + } + return body; +} + +/** + * Parses URL encoded body + * + * @param {Object} body body object from request. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @returns {String} snippet of the body generation + */ +function parseURLEncodedBody (body, indentation, bodyTrim) { + let enabledBodyList = _.reject(body.members, 'disabled'), + bodySnippet = ''; + if (!_.isEmpty(enabledBodyList)) { + let bodyDataMap = _.map(enabledBodyList, (data) => { + return `${indentation}"${sanitizeString(data.key, bodyTrim)}" = "${sanitizeString(data.value, bodyTrim)}"`; + }); + bodySnippet += `c(\n${bodyDataMap.join(',\n')}\n)`; + } + return bodySnippet; +} + +/** + * builds a single data param + * + * @param {Object} data data of the param. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @returns {String} snippet of the body generation + */ +function buildFormDataParam (data, indentation, bodyTrim) { + return `${indentation}"${sanitizeString(data.key, bodyTrim)}" = "${sanitizeString(data.value, bodyTrim)}"`; +} + +/** + * builds a data param for a file + * + * @param {Object} data item from the array of form data (key value). + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @param {number} index index of the current file + * @returns {String} snippet of file uploading + */ +function buildFormDataParamFile (data, indentation, bodyTrim, index) { + return `file${index} = fileUpload(\n` + + `${indentation.repeat(1)}filename = path.expand('${sanitizeString(data.src, bodyTrim)}')` + + ')\n'; +} + +/** + * builds a data param + * + * @param {Object} body body object from request. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @returns {String} snippet of the body generation + */ +function parseFormData (body, indentation, bodyTrim) { + let enabledBodyList = _.reject(body.members, 'disabled'), + numberOfFiles = 0, + bodySnippet = '', + fileSnippet = ''; + if (!_.isEmpty(enabledBodyList)) { + let formDataFile, formData, bodyDataMap; + formDataFile = enabledBodyList.filter((param) => { + return param.type === 'file'; + }); + formData = enabledBodyList.filter((param) => { + return param.type !== 'file'; + }); + bodyDataMap = _.map(formData, (data) => { + return buildFormDataParam(data, indentation, bodyTrim); + }); + numberOfFiles = formDataFile.length; + _.forEach(formDataFile, (data, index) => { + fileSnippet += buildFormDataParamFile(data, indentation, bodyTrim, index); + }); + if (bodyDataMap.length > 0) { + bodySnippet += `c(\n${bodyDataMap.join(',\n')}\n)`; + } + } + return {bodySnippet, fileSnippet, numberOfFiles}; +} + +/** + * Parses Raw data + * + * @param {Object} body body object from request. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @param {String} contentType Content type of the body being sent + * @returns {String} snippet of the body generation + */ +function parseRawBody (body, indentation, bodyTrim, contentType) { + let bodySnippet = ''; + if (contentType && (contentType === 'application/json' || contentType.match(/\+json$/))) { + try { + let jsonBody = JSON.parse(body); + bodySnippet += `"${sanitizeString(JSON.stringify(jsonBody, null, indentation.length), bodyTrim)}"`; + } + catch (error) { + bodySnippet += `"${sanitizeString(body.toString(), bodyTrim)}"`; + } + } + else { + bodySnippet += JSON.stringify(body.toString()); + } + return bodySnippet; +} + +/** + * Parses Body of graphql + * + * @param {Object} body body object from request. + * @param {boolean} bodyTrim trim body option + * @return {String} the data for a binary file + */ +function parseGraphQL (body, bodyTrim) { + const query = body.query; + let bodySnippet = '', + graphqlVariables; + try { + graphqlVariables = JSON.parse(body.variables); + } + catch (e) { + graphqlVariables = {}; + } + bodySnippet = `'${sanitizeString(JSON.stringify({ + query: query, + variables: graphqlVariables + }), bodyTrim)}';`; + return bodySnippet; +} + +/** + * Parses Body of file + * + * @return {String} the data for a binary file + */ +function parseFromFile () { + return '"";'; +} + + +/** + * Parses Body from the Request + * + * @param {Object} body body object from request. + * @param {String} indentation indentation to be added to the snippet + * @param {boolean} bodyTrim trim body option + * @param {String} contentType Content type of the body being sent + */ +function processBodyModes (body, indentation, bodyTrim, contentType) { + let bodySnippet = ''; + switch (body.mode) { + case 'urlencoded': { + bodySnippet = parseURLEncodedBody(body.urlencoded, indentation, bodyTrim); + return bodySnippet === '' ? '' : `params = ${bodySnippet}\n`; + } + case 'raw': { + bodySnippet = parseRawBody(body.raw, indentation, bodyTrim, contentType); + return bodySnippet === '' ? '' : `params = ${bodySnippet}\n`; + } + case 'graphql': { + bodySnippet = parseGraphQL(body.graphql, bodyTrim); + return bodySnippet === '' ? '' : `params = ${bodySnippet}\n`; + } + case 'formdata': { + let formData = parseFormData(body.formdata, indentation, bodyTrim), + formParamsSnippet = formData.bodySnippet === '' ? '' : `params = ${formData.bodySnippet}\n`; + return { bodySnippet: formParamsSnippet, + fileSnippet: formData.fileSnippet, + numberOfFiles: formData.numberOfFiles}; + } + case 'file': { + bodySnippet = parseFromFile(); + return bodySnippet === '' ? '' : `params = ${bodySnippet}\n`; + } + default: { + bodySnippet = parseRawBody(body.raw, indentation, bodyTrim, contentType); + return bodySnippet === '' ? '' : `params = ${bodySnippet}\n`; + } + } +} + +/** +* parses a body to the corresponding snippet +* +* @param {object} body - postman request body +* @param {string} indentation - indentation character +* @param {boolean} bodyTrim trim body option +* @param {String} contentType Content type of the body being sent +* @returns {String/Object} snippet of the body generation or object for files information +*/ +function parseBody (body, indentation, bodyTrim, contentType) { + let snippet = ''; + if (body && !_.isEmpty(body)) { + body = solveMultiFile(body); + return processBodyModes(body, indentation, bodyTrim, contentType); + } + return snippet; +} + +module.exports = { + parseBody, + parseURLEncodedBody, + parseFormData, + parseRawBody, + parseGraphQL, + buildFormDataParamFile +}; diff --git a/codegens/r-rcurl/lib/util/sanitize.js b/codegens/r-rcurl/lib/util/sanitize.js new file mode 100644 index 000000000..4e01496ff --- /dev/null +++ b/codegens/r-rcurl/lib/util/sanitize.js @@ -0,0 +1,87 @@ +module.exports = { + /** + * used to sanitize eg: trim, handle escape characters + * + * @param {String} inputString - input + * @param {Boolean} inputTrim - whether to trim the input + * @returns {String} + */ + sanitizeString: function (inputString, inputTrim) { + if (typeof inputString !== 'string') { + return ''; + } + inputString = inputTrim && typeof inputTrim === 'boolean' ? inputString.trim() : inputString; + return inputString.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + }, + + /** + * sanitizes input options + * + * @param {Object} options - Options provided by the user + * @param {Array} optionsArray - options array received from getOptions function + * + * @returns {Object} - Sanitized options object + */ + sanitizeOptions: function (options, optionsArray) { + var result = {}, + defaultOptions = {}, + id; + + optionsArray.forEach((option) => { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + + for (id in options) { + if (options.hasOwnProperty(id)) { + if (defaultOptions[id] === undefined) { + continue; + } + switch (defaultOptions[id].type) { + case 'boolean': + if (typeof options[id] !== 'boolean') { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'positiveInteger': + if (typeof options[id] !== 'number' || options[id] < 0) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'enum': + if (!defaultOptions[id].availableOptions.includes(options[id])) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + default: + result[id] = options[id]; + } + } + } + + for (id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + if (result[id] === undefined) { + result[id] = defaultOptions[id].default; + } + } + } + + return result; + } + +}; diff --git a/codegens/r-rcurl/npm-shrinkwrap.json b/codegens/r-rcurl/npm-shrinkwrap.json new file mode 100644 index 000000000..5901badd2 --- /dev/null +++ b/codegens/r-rcurl/npm-shrinkwrap.json @@ -0,0 +1,5 @@ +{ + "name": "@postman/codegen-r-rcurl", + "version": "0.0.1", + "lockfileVersion": 1 +} diff --git a/codegens/r-rcurl/npm/test-lint.js b/codegens/r-rcurl/npm/test-lint.js new file mode 100644 index 000000000..2f4db0cb8 --- /dev/null +++ b/codegens/r-rcurl/npm/test-lint.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +var shell = require('shelljs'), + chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/r-rcurl/npm/test-newman.js b/codegens/r-rcurl/npm/test-newman.js new file mode 100644 index 000000000..cf3ddf1e6 --- /dev/null +++ b/codegens/r-rcurl/npm/test-newman.js @@ -0,0 +1,60 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'newman'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running newman tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + return undefined; + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/r-rcurl/npm/test-unit.js b/codegens/r-rcurl/npm/test-unit.js new file mode 100644 index 000000000..b93a5cba1 --- /dev/null +++ b/codegens/r-rcurl/npm/test-unit.js @@ -0,0 +1,60 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running unit tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + return undefined; + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/r-rcurl/npm/test.js b/codegens/r-rcurl/npm/test.js new file mode 100644 index 000000000..07be3d9ca --- /dev/null +++ b/codegens/r-rcurl/npm/test.js @@ -0,0 +1,19 @@ +#!/usr/bin/env node +var chalk = require('chalk'), + exit = require('shelljs').exit, + prettyms = require('pretty-ms'), + startedAt = Date.now(), + name = require('../package.json').name; + +require('async').series([ + require('./test-lint'), + require('./test-newman'), + // Add a separate folder for every new suite of tests + require('./test-unit') + // require('./test-browser') + // require('./test-integration') +], function (code) { + // eslint-disable-next-line max-len + console.info(chalk[code ? 'red' : 'green'](`\n${name}: duration ${prettyms(Date.now() - startedAt)}\n${name}: ${code ? 'not ok' : 'ok'}!`)); + exit(code && (typeof code === 'number' ? code : 1) || 0); +}); diff --git a/codegens/r-rcurl/package.json b/codegens/r-rcurl/package.json new file mode 100644 index 000000000..034b606d0 --- /dev/null +++ b/codegens/r-rcurl/package.json @@ -0,0 +1,34 @@ +{ + "name": "@postman/codegen-r-rcurl", + "version": "0.0.1", + "description": "Converts Postman SDK Requests to R-RCurl code snippet", + "main": "index.js", + "com_postman_plugin": { + "type": "code_generator", + "lang": "R", + "variant": "RCurl", + "syntax_mode": "R" + }, + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "node npm/test.js", + "test-lint": "node npm/test-lint.js", + "test-unit": "node npm/test-unit.js", + "test-newman": "node npm/test-newman.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "Postman Labs ", + "license": "Apache-2.0", + "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/r-curl", + "dependencies": {}, + "devDependencies": {}, + "engines": { + "node": ">=8" + } +} diff --git a/codegens/r-rcurl/test/ci-install.sh b/codegens/r-rcurl/test/ci-install.sh new file mode 100755 index 000000000..69b15e991 --- /dev/null +++ b/codegens/r-rcurl/test/ci-install.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/r-httr" +sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 +sudo add-apt-repository 'deb https://cloud.r-project.org/bin/linux/ubuntu focal-cran40/' +sudo apt-get update +sudo apt-get install r-base libcurl4-gnutls-dev +sudo R --vanilla -e 'install.packages("RCurl", version="1.98.1.6", repos="http://cran.us.r-project.org")' diff --git a/codegens/r-rcurl/test/newman/newman.test.js b/codegens/r-rcurl/test/newman/newman.test.js new file mode 100644 index 000000000..f870061d6 --- /dev/null +++ b/codegens/r-rcurl/test/newman/newman.test.js @@ -0,0 +1,21 @@ +var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, + convert = require('../../index').convert; + + +describe('R-Rcurl Converter', function () { + describe('Convert for different types of request', function () { + var testConfig = { + runScript: 'Rscript codesnippet.r', + fileName: 'codesnippet.r', + compileScript: null, + skipCollections: ['unsupportedMethods'] + + }, + options = { + indentType: 'Space', + indentCount: 4, + ignoreWarnings: true + }; + runNewmanTest(convert, options, testConfig); + }); +}); diff --git a/codegens/r-rcurl/test/unit/convert.test.js b/codegens/r-rcurl/test/unit/convert.test.js new file mode 100644 index 000000000..e8e8d28ef --- /dev/null +++ b/codegens/r-rcurl/test/unit/convert.test.js @@ -0,0 +1,144 @@ +var expect = require('chai').expect, + { convert } = require('../../index'), + { Collection } = require('postman-collection/lib/collection/collection'), + fs = require('fs'), + path = require('path'); + +describe('convert function', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + + it('should convert requests with default options', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + }); + }); + done(); + }); + + it('should convert requests with ignore warnigns options', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { ignoreWarnings: true}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.include('options(warn=-1)'); + }); + }); + done(); + }); + + it('should convert requests with ignore warnigns options in false', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { ignoreWarnings: false}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.not.include('options(warn=-1)'); + }); + }); + done(); + }); + + it('should convert requests with ignore warnigns options not present', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, {}, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.not.include('options(warn=-1)'); + }); + }); + done(); + }); + + it('should convert requests with requestTimeout option set as 500', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { requestTimeout: 500 }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.include('timeout.ms = 500'); + }); + }); + done(); + }); + + it('should convert requests with requestTimeout option not present', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.not.include('\'timeout\''); + }); + }); + done(); + }); + + it('should convert requests with requestTimeout option set as 0', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { requestTimeout: 0 }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.not.include('\'timeout\' => 0'); + }); + }); + done(); + }); + + it('should convert requests with followRedirect option set as false', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { followRedirect: false }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.not.include('followlocation = FALSE'); + }); + }); + done(); + }); + + it('should convert requests with followRedirect option set as true', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { followRedirect: true }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.include('followlocation = TRUE'); + }); + }); + done(); + }); + + it('should convert requests with followRedirect option not present', function (done) { + collection.items.members.forEach((item) => { + convert(item.request, { }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + expect(snippet).to.include('followlocation'); + }); + }); + done(); + }); + + it('should throw an error when callback is not a function', function () { + expect(function () { convert({}, {}); }) + .to.throw('R-Rcurl~convert: Callback is not a function'); + }); +}); diff --git a/codegens/r-rcurl/test/unit/fixtures/sample_collection.json b/codegens/r-rcurl/test/unit/fixtures/sample_collection.json new file mode 100644 index 000000000..3977935be --- /dev/null +++ b/codegens/r-rcurl/test/unit/fixtures/sample_collection.json @@ -0,0 +1,1738 @@ +{ + "info": { + "name": "Code-Gen Test R RCurl", + "_postman_id": "43058ce7-94da-0f0f-366e-22b77d566316", + "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Request Headers", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "try {", + " tests[\"Body contains headers\"] = responseBody.has(\"headers\");", + " responseJSON = JSON.parse(responseBody);", + " tests[\"Header contains host\"] = \"host\" in responseJSON.headers;", + " tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;", + "}", + "catch (e) { }", + "", + "", + "", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "testing", + "value": "'singlequotes'" + }, + { + "key": "TEST", + "value": "\"doublequotes\"" + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "Request Headers (With special Characters)", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "try {", + " tests[\"Body contains headers\"] = responseBody.has(\"headers\");", + " responseJSON = JSON.parse(responseBody);", + " tests[\"Header contains host\"] = \"host\" in responseJSON.headers;", + " tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;", + "}", + "catch (e) { }", + "", + "", + "", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "TEST", + "value": "@#$%^&*()" + }, + { + "key": "more", + "value": ",./';[]}{\":?><|" + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "Request Headers with disabled headers", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "try {", + " tests[\"Body contains headers\"] = responseBody.has(\"headers\");", + " responseJSON = JSON.parse(responseBody);", + " tests[\"Header contains host\"] = \"host\" in responseJSON.headers;", + " tests[\"Header contains test parameter sent as part of request header\"] = \"my-sample-header\" in responseJSON.headers;", + "}", + "catch (e) { }", + "", + "", + "", + "" + ] + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "my-sample-header", + "value": "Lorem ipsum dolor sit amet" + }, + { + "key": "not-disabled-header", + "value": "ENABLED" + }, + { + "key": "disabled header", + "value": "DISABLED", + "disabled": true + } + ], + "body": {}, + "url": { + "raw": "https://postman-echo.com/headers", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "headers" + ] + }, + "description": "A `GET` request to this endpoint returns the list of all request headers as part of the response JSON.\nIn Postman, sending your own set of headers through the [Headers tab](https://www.getpostman.com/docs/requests#headers?source=echo-collection-app-onboarding) will reveal the headers as part of the response." + }, + "response": [] + }, + { + "name": "GET Request with disabled query", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "tests['response json contains headers'] = _.has(responseJSON, 'headers');", + "tests['response json contains args'] = _.has(responseJSON, 'args');", + "tests['response json contains url'] = _.has(responseJSON, 'url');", + "", + "tests['args key contains argument passed as url parameter'] = ('test' in responseJSON.args);", + "tests['args passed via request url params has value \"123\"'] = (_.get(responseJSON, 'args.test') === \"123\");" + ] + } + } + ], + "request": { + "method": "GET", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/get?test=123&anotherone=232", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "test", + "value": "123", + "equals": true + }, + { + "key": "anotherone", + "value": "232", + "equals": true + }, + { + "key": "anotheroneone", + "value": "sdfsdf", + "equals": true, + "disabled": true + } + ] + }, + "description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested." + }, + "response": [] + }, + { + "name": "POST form data with special characters Copy", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "pl", + "value": "'a'", + "type": "text" + }, + { + "key": "qu", + "value": "\"b\"", + "type": "text" + }, + { + "key": "hdjkljh ", + "value": "c ", + "type": "text" + }, + { + "key": "sa", + "value": "d", + "type": "text" + }, + { + "key": "Special ", + "value": "!@#$%&*()^_+=`~ ", + "type": "text" + }, + { + "key": "Not Select", + "value": "Disabled", + "type": "text", + "disabled": true + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Resolve URL (Quotes + Special Characters) Copy", + "request": { + "method": "POST", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/:action?a=!@$^*()_-`%26&b=,./';[]}{\":/?><||\\", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + ":action" + ], + "query": [ + { + "key": "a", + "value": "!@$^*()_-`%26", + "equals": true + }, + { + "key": "b", + "value": ",./';[]}{\":/?><||\\", + "equals": true + } + ], + "variable": [ + { + "key": "action", + "value": "post" + } + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "POST Raw Text", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST urlencoded data with disabled entries", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "1", + "value": "'a'", + "description": "", + "type": "text" + }, + { + "key": "2", + "value": "\"b\"", + "description": "", + "type": "text" + }, + { + "key": "'3'", + "value": "c", + "description": "", + "type": "text" + }, + { + "key": "\"4\" ", + "value": "d ", + "description": "", + "type": "text" + }, + { + "key": "Special", + "value": "!@#$%&*()^_=`~", + "description": "", + "type": "text" + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\", + "description": "", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST json with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [ + { + "id": "0bfab08c-cc5a-4bb0-85f2-699383707fe4", + "name": "POST json with raw", + "originalRequest": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"json\": \"Test-Test\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "Lets a server whitelist headers that browsers are allowed to access." + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Length", + "value": "385", + "name": "Content-Length", + "description": "The length of the response body in octets (8-bit bytes)" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Wed, 07 Feb 2018 10:06:15 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "ETag", + "value": "W/\"215-u7EU1nFtauIn0/aVifjuXA\"", + "name": "ETag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Vary", + "value": "X-HTTP-Method-Override, Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + }, + { + "key": "set-cookie", + "value": "sails.sid=s%3AxRBxgrc9M-jKK_l1mX3y3rM_ry8wYLz4.Of4qpOzd9hi6uO0sAQIj%2Bxs2VeppWxYjJa4OpIW3PKg; Path=/; HttpOnly", + "name": "set-cookie", + "description": "an HTTP cookie" + } + ], + "cookie": [ + { + "expires": "Tue Jan 19 2038 08:44:07 GMT+0530 (IST)", + "httpOnly": true, + "domain": "postman-echo.com", + "path": "/", + "secure": false, + "value": "s%3AxRBxgrc9M-jKK_l1mX3y3rM_ry8wYLz4.Of4qpOzd9hi6uO0sAQIj%2Bxs2VeppWxYjJa4OpIW3PKg", + "key": "sails.sid" + } + ], + "responseTime": null, + "body": "{\"args\":{},\"data\":\"{\\n \\\"json\\\": \\\"Test-Test\\\"\\n}\",\"files\":{},\"form\":{},\"headers\":{\"host\":\"postman-echo.com\",\"content-length\":\"25\",\"accept\":\"*/*\",\"accept-encoding\":\"gzip, deflate\",\"cache-control\":\"no-cache\",\"content-type\":\"text/plain\",\"cookie\":\"sails.sid=s%3AkOgtF1XmXtVFx-Eg3S7-37BKKaMqMDPe.hnwldNwyvsaASUiRR0Y0vcowadkMXO4HMegTeVIPgqo\",\"postman-token\":\"2ced782f-a141-428e-8af6-04ce954a77d5\",\"user-agent\":\"PostmanRuntime/7.1.1\",\"x-forwarded-port\":\"443\",\"x-forwarded-proto\":\"https\"},\"json\":null,\"url\":\"https://postman-echo.com/post\"}" + } + ] + }, + { + "name": "POST javascript with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/javascript" + } + ], + "body": { + "mode": "raw", + "raw": "var val = 6;\nconsole.log(val);" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/xml with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/xml" + } + ], + "body": { + "mode": "raw", + "raw": "\n\tTest Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "POST text/html with raw", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "text/html" + } + ], + "body": { + "mode": "raw", + "raw": "\n Test Test\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Resolve URL", + "request": { + "method": "POST", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/:action?a=''&b=\"\"", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + ":action" + ], + "query": [ + { + "key": "a", + "value": "''", + "equals": true + }, + { + "key": "b", + "value": "\"\"", + "equals": true + }, + { + "key": "more", + "value": "", + "equals": true, + "disabled": true + } + ], + "variable": [ + { + "key": "action", + "value": "post" + } + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PUT Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." + }, + "url": { + "raw": "https://postman-echo.com/put", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "put" + ] + }, + "description": "The HTTP `PUT` request method is similar to HTTP `POST`. It too is meant to \ntransfer data to a server (and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `PUT` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following \nraw HTTP request,\n\n> PUT /hi/there?hand=wave\n>\n> \n\n\n" + }, + "response": [] + }, + { + "name": "PATCH Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." + }, + "url": { + "raw": "https://postman-echo.com/patch", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "patch" + ] + }, + "description": "The HTTP `PATCH` method is used to update resources on a server. The exact\nuse of `PATCH` requests depends on the server in question. There are a number\nof server implementations which handle `PATCH` differently. Technically, \n`PATCH` supports both Query String parameters and a Request Body.\n\nThis endpoint accepts an HTTP `PATCH` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "DELETE Request", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has PUT data'] = _.has(responseJSON, 'data');", + "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" + ] + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Donec fermentum, nisi sed cursus eleifend, nulla tortor ultricies tellus, ut vehicula orci arcu ut velit. In volutpat egestas dapibus.\nMorbi condimentum vestibulum sapien. Etiam dignissim diam quis eros lobortis gravida vel lobortis est. Etiam gravida sed." + }, + "url": { + "raw": "https://postman-echo.com/delete", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "delete" + ] + }, + "description": "The HTTP `DELETE` method is used to delete resources on a server. The exact\nuse of `DELETE` requests depends on the server implementation. In general, \n`DELETE` requests support both, Query String parameters as well as a Request \nBody.\n\nThis endpoint accepts an HTTP `DELETE` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." + }, + "response": [] + }, + { + "name": "OPTIONS to postman echo", + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "var responseJSON;", + "", + "try { ", + " responseJSON = JSON.parse(responseBody); ", + " tests['response is valid JSON'] = true;", + "}", + "catch (e) { ", + " responseJSON = {}; ", + " tests['response is valid JSON'] = false;", + "}", + "", + "", + "tests['response has post data'] = _.has(responseJSON, 'data');", + "tests['response matches the data posted'] = (responseJSON.data && responseJSON.data.length === 256);", + "", + "tests[\"content-type equals text/plain\"] = responseJSON && responseJSON.headers && (responseJSON.headers[\"content-type\"] === 'text/plain');" + ] + } + } + ], + "request": { + "auth": { + "type": "noauth" + }, + "method": "OPTIONS", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "LINK request", + "request": { + "method": "LINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "UNLINK request", + "request": { + "method": "UNLINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "LOCK request", + "request": { + "method": "LOCK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "UNLOCK request", + "request": { + "method": "UNLOCK", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PROPFIND request", + "request": { + "method": "PROPFIND", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "VIEW request", + "request": { + "method": "VIEW", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PURGE Request", + "request": { + "method": "PURGE", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "COPY Request", + "request": { + "method": "COPY", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + }, + "description": "" + }, + "response": [ + { + "id": "47494bfb-6d21-4f1f-ace0-f88e5e1ac05d", + "name": "COPY Request", + "originalRequest": { + "method": "COPY", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "_postman_previewtype": "text", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "Lets a server whitelist headers that browsers are allowed to access." + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Length", + "value": "59", + "name": "Content-Length", + "description": "The length of the response body in octets (8-bit bytes)" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Mon, 05 Feb 2018 07:48:41 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "ETag", + "value": "W/\"af-MmpVeTvfnSW88c4riXD0uw\"", + "name": "ETag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Vary", + "value": "Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + } + ], + "cookie": [], + "responseTime": 378, + "body": "{\n \"status\": 200,\n \"method\": \"COPY\"\n}" + } + ] + }, + { + "name": "POST binary file", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/x-www-form-urlencoded", + "type": "text" + } + ], + "body": { + "mode": "file", + "file": {} + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "POST form data with file", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "test-file", + "type": "file", + "src": "" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "POST graphql body(json) with raw", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "graphql", + "graphql": { + "query": "{\n findScenes(\n filter: {per_page: 0}\n scene_filter: {is_missing: \"performers\"}){\n count\n scenes {\n id\n title\n path\n }\n }\n}", + "variables": "{\n\t\"variable_key\": \"variable_value\"\n}" + } + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "POST empty Formdata", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "Multiple headers with same name", + "request": { + "method": "GET", + "header": [ + { + "key": "key", + "value": "value1", + "type": "text" + }, + { + "key": "key", + "value": "value2", + "type": "text" + } + ], + "url": { + "raw": "https://postman-echo.com/get", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "get" + ] + } + }, + "response": [] + }, + { + "name": "POST multipart/form-data with text parameters", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "pl", + "value": "'a'", + "type": "text" + }, + { + "key": "qu", + "value": "\"b\"", + "type": "text" + }, + { + "key": "sa", + "value": "d", + "type": "text" + }, + { + "key": "Special", + "value": "!@#$%&*()^_+=`~", + "type": "text" + }, + { + "key": "Not Select", + "value": "Disabled", + "type": "text", + "disabled": true + }, + { + "key": "more", + "value": ",./';[]}{\":?><|\\\\", + "type": "text" + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + }, + { + "name": "Single/multiple file upload via form-data", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "single file", + "value": "", + "type": "file", + "src": "/Users/luis.tejeda/Documents/Source/GitHub/postmanlabs/postman-code-generators/dummyFile1.txt" + }, + { + "key": "multiple files", + "value": "", + "type": "file", + "src": [ + "/Users/luis.tejeda/Documents/Source/GitHub/postmanlabs/postman-code-generators/dummyFile2.txt", + "/Users/luis.tejeda/Documents/Source/GitHub/postmanlabs/postman-code-generators/dummyFile3.txt" + ] + } + ] + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + } + }, + { + "name": "POST javascript with raw", + "event": [ + { + "listen": "test", + "script": { + "id": "d211bdad-60b3-4cd6-869f-853377bf03ef", + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/javascript" + } + ], + "body": { + "mode": "raw", + "raw": "var val = 6;\nconsole.log(val);console.log('$text\r\n');\n" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + }, + "description": "The HTTP `POST` request method is meant to transfer data to a server \n(and elicit a response). What data is returned depends on the implementation\nof the server.\n\nA `POST` request can pass parameters to the server using \"Query String \nParameters\", as well as the Request Body. For example, in the following request,\n\n> POST /hi/there?hand=wave\n>\n> \n\nThe parameter \"hand\" has the value \"wave\". The request body can be in multiple\nformats. These formats are defined by the MIME type of the request. The MIME \nType can be set using the ``Content-Type`` HTTP header. The most commonly used \nMIME types are:\n\n* `multipart/form-data`\n* `application/x-www-form-urlencoded`\n* `application/json`\n\nThis endpoint echoes the HTTP headers, request parameters, the contents of\nthe request body and the complete URI requested." + }, + "response": [] + }, + { + "name": "Follow Redirects", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/redirect/302/1/?to=https://postman-echo.com/get", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "redirect", + "302", + "1", + "" + ], + "query": [ + { + "key": "to", + "value": "https://postman-echo.com/get" + } + ] + } + }, + "response": [] + } + ] +} diff --git a/codegens/r-rcurl/test/unit/options.test.js b/codegens/r-rcurl/test/unit/options.test.js new file mode 100644 index 000000000..551e896ee --- /dev/null +++ b/codegens/r-rcurl/test/unit/options.test.js @@ -0,0 +1,34 @@ +var expect = require('chai').expect, + getOptions = require('../../index').getOptions, + availableOptions = [{ + 0: 'indentCount' + }, + { + 1: 'indentType' + }, + { + 2: 'trimRequestBody' + }, + { + 3: 'requestTimeout' + }, + { + 4: 'followRedirect' + }, + { + 5: 'ignoreWarnings' + } + ]; + +describe('getOptions function', function () { + it('should return array of options for R-RCurl converter', function () { + expect(getOptions()).to.be.an('array'); + }); + + it('should return all the valid options', function () { + let options = getOptions(); + availableOptions.forEach((availableOption, index) => { + expect(options[index]).to.have.property('id', availableOption[index]); + }); + }); +}); diff --git a/codegens/r-rcurl/test/unit/parseBody.test.js b/codegens/r-rcurl/test/unit/parseBody.test.js new file mode 100644 index 000000000..97861658f --- /dev/null +++ b/codegens/r-rcurl/test/unit/parseBody.test.js @@ -0,0 +1,204 @@ +var expect = require('chai').expect, + { Collection } = require('postman-collection/lib/collection/collection'), + fs = require('fs'), + path = require('path'), + { + parseURLEncodedBody, + parseBody, + parseFormData, + parseRawBody, + parseGraphQL, + buildFormDataParamFile + } = require('../../lib/util/parseBody'), + collectionsPath = './fixtures'; + +describe('parseURLEncodedBody method', function () { + it('should return form-url-encoded params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[7].request.body.urlencoded, + indentation = ' ', + bodyTrim = false, + expectedBody = 'c(\n' + + ' "1" = "\'a\'",\n' + + ' "2" = "\\"b\\"",\n' + + ' "\'3\'" = "c",\n' + + ' "\\"4\\" " = "d ",\n' + + ' "Special" = "!@#$%&*()^_=`~",\n' + + ' "more" = ",./\';[]}{\\":?><|\\\\\\\\"\n' + + ')', + result = parseURLEncodedBody(body, indentation, bodyTrim); + expect(result).to.equal(expectedBody); + }); + it('should return empty snippet for emtpy form-url-encoded params', function () { + const indentation = ' ', + bodyTrim = false, + expectedBody = '', + result = parseURLEncodedBody({ members: []}, indentation, bodyTrim); + expect(result).to.equal(expectedBody); + }); +}); + +describe('parseFormData method', function () { + it('should return form data params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[4].request.body.formdata, + indentation = ' ', + bodyTrim = false, + expectedBody = 'c(\n' + + ' "pl" = "\'a\'",\n' + + ' "qu" = "\\"b\\"",\n' + + ' "hdjkljh " = "c ",\n' + + ' "sa" = "d",\n' + + ' "Special " = "!@#$%&*()^_+=`~ ",\n' + + ' "more" = ",./\';[]}{\\":?><|\\\\\\\\"\n' + + ')', + result = parseFormData(body, indentation, bodyTrim); + expect(result.bodySnippet).to.equal(expectedBody); + }); + + it('should return empty snippet for emtpy formdata params', function () { + const indentation = ' ', + bodyTrim = false, + expectedBody = '', + result = parseFormData({ members: []}, indentation, bodyTrim); + expect(result.bodySnippet).to.equal(expectedBody); + }); + + it('should return form data params file', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[26].request.body.formdata, + indentation = ' ', + bodyTrim = false, + result = parseFormData(body, indentation, bodyTrim); + expect(result.numberOfFiles).to.equal(1); + expect(result.fileSnippet).to.equal('file0 = fileUpload(\n filename = path.expand(\'\'))\n'); + }); +}); + +describe('parseRawBody method', function () { + it('should return formData json params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[8].request.body.raw, + indentation = ' ', + bodyTrim = false, + expectedBody = '"{\n' + + ' \\"json\\": \\"Test-Test\\"\n' + + '}"', + result = parseRawBody(body, indentation, bodyTrim, 'application/json'); + expect(result).to.equal(expectedBody); + }); +}); + +describe('parseGraphQL method', function () { + it('should return graphql json params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[27].request.body.graphql, + bodyTrim = false, + expectedBody = '\'{\\"query\\":\\"{\\\\n findScenes(\\\\n filter: {per_page: 0}\\\\n ' + + ' scene_filter: {is_missing: \\\\\\"performers\\\\\\"}){\\\\n count\\\\n scenes' + + ' {\\\\n id\\\\n title\\\\n path\\\\n }\\\\n }\\\\n}\\",\\"variables\\":' + + '{\\"variable_key\\":\\"variable_value\\"}}\';', + result = parseGraphQL(body, bodyTrim); + expect(result).to.equal(expectedBody); + }); +}); + +describe('parseBody method', function () { + it('should return form-url-encoded params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[7].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'params = c(\n' + + ' "1" = "\'a\'",\n' + + ' "2" = "\\"b\\"",\n' + + ' "\'3\'" = "c",\n' + + ' "\\"4\\" " = "d ",\n' + + ' "Special" = "!@#$%&*()^_=`~",\n' + + ' "more" = ",./\';[]}{\\":?><|\\\\\\\\"\n' + + ')\n', + result = parseBody(body, indentation, bodyTrim); + expect(result).to.equal(expectedBody); + }); + + it('should return form data params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[4].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'params = c(\n' + + ' "pl" = "\'a\'",\n' + + ' "qu" = "\\"b\\"",\n' + + ' "hdjkljh " = "c ",\n' + + ' "sa" = "d",\n' + + ' "Special " = "!@#$%&*()^_+=`~ ",\n' + + ' "more" = ",./\';[]}{\\":?><|\\\\\\\\"\n' + + ')\n', + result = parseBody(body, indentation, bodyTrim); + expect(result.bodySnippet).to.equal(expectedBody); + }); + + it('should return raw json params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[8].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'params = "{\n' + + ' \\"json\\": \\"Test-Test\\"\n' + + '}"\n', + result = parseBody(body, indentation, bodyTrim, 'application/json'); + expect(result).to.equal(expectedBody); + }); + + it('should return raw string params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[6].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'params = "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. ' + + 'Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\\nMaecenas consequat elementum elit,' + + ' id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat."\n', + result = parseBody(body, indentation, bodyTrim); + expect(result).to.equal(expectedBody); + }); + + it('should return graphql params', function () { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, collectionsPath, './sample_collection.json').toString()))), + body = collection.items.members[27].request.body, + indentation = ' ', + bodyTrim = false, + expectedBody = 'params = \'{\\"query\\":\\"{\\\\n findScenes(\\\\n filter: {per_page: 0}\\\\n ' + + ' scene_filter: {is_missing: \\\\\\"performers\\\\\\"}){\\\\n count\\\\n scenes' + + ' {\\\\n id\\\\n title\\\\n path\\\\n }\\\\n }\\\\n}\\",\\"variables\\":' + + '{\\"variable_key\\":\\"variable_value\\"}}\';\n', + result = parseBody(body, indentation, bodyTrim, 'graphql'); + expect(result).to.equal(expectedBody); + + }); +}); + +describe('buildFormDataParamFile method', function () { + it('should return a snippet for file var creation"', function () { + const expected = 'file0 = fileUpload(\n' + + ' filename = path.expand(\'/Users/name/dummyFile1.txt\'))\n', + res = buildFormDataParamFile({ src: '/Users/name/dummyFile1.txt'}, ' ', true, 0); + expect(expected).to.equal(res); + }); + + it('should return a snippet for file var creation index 1"', function () { + const expected = 'file1 = fileUpload(\n' + + ' filename = path.expand(\'/Users/name/dummyFile1.txt\'))\n', + res = buildFormDataParamFile({ src: '/Users/name/dummyFile1.txt'}, ' ', true, 1); + expect(expected).to.equal(res); + }); +}); diff --git a/codegens/r-rcurl/test/unit/rRcurl.test.js b/codegens/r-rcurl/test/unit/rRcurl.test.js new file mode 100644 index 000000000..ab2194a00 --- /dev/null +++ b/codegens/r-rcurl/test/unit/rRcurl.test.js @@ -0,0 +1,501 @@ +var expect = require('chai').expect, + { Collection } = require('postman-collection/lib/collection/collection'), + { Request } = require('postman-collection/lib/collection/request'), + fs = require('fs'), + path = require('path'), + { + convert, + getSnippetHeaders, + getSnippetPostFormInParams, + getSnippetGetURL, + getSnippetRequest, + getSnippetPostFormInOptions, + addContentTypeHeader, + buildOptionsSnippet, + groupHeadersSameKey, + getIndentation, + getSnippetPut, + getSnippetDelete, + getSnippetURLContent + } = require('../../lib/rRcurl'); + +describe('convert function', function () { + + it('should convert a simple get request', function (done) { + const collection = new Collection(JSON.parse( + fs.readFileSync(path.resolve(__dirname, './fixtures/sample_collection.json').toString()))); + collection.items.members.forEach((item) => { + convert(item.request, { }, function (err, snippet) { + if (err) { + console.error(err); + } + expect(snippet).to.not.be.empty; + }); + }); + done(); + }); + + it('should throw an error when callback is not a function', function () { + expect(function () { convert({}, {}); }) + .to.throw('R-Rcurl~convert: Callback is not a function'); + }); +}); + +describe('getSnippetHeaders function', function () { + + it('should generate headers declaration snippet', function () { + const expected = 'headers = c(\n "\\"1\\"" = "\'a\'",\n "\\"2\\"" = "\\"b\\""\n)\n', + res = getSnippetHeaders([{ key: '"1"', value: '\'a\''}, { key: '"2"', value: '"b"'}], ' '); + expect(res).to.equal(expected); + }); + + it('should generate empty headers declaration snippet without headers', function () { + const expected = '', + res = getSnippetHeaders([ ], ' '); + expect(res).to.equal(expected); + }); + + it('should generate headers declaration snippet with empty indentation', function () { + const expected = 'headers = c(\n"\\"1\\"" = "\'a\'",\n"\\"2\\"" = "\\"b\\""\n)\n', + res = getSnippetHeaders([{ key: '"1"', value: '\'a\''}, { key: '"2"', value: '"b"'}], ''); + expect(res).to.equal(expected); + }); + + it('should return an string representing the headers special characters', function () { + const headersArray = + [ + { + key: 'my-sample-header', + value: 'Lorem ipsum dolor sit amet' + }, + { + key: 'TEST', + value: '@#$%^&*()' + }, + { + key: 'more', + value: ',./\';[]}{\\":?><|' + } + ], + expectedString = 'headers = c(\n "my-sample-header" = "Lorem ipsum dolor sit amet",\n' + + ' "TEST" = "@#$%^&*()",\n "more" = ",./\';[]}{\\\\\\":?><|"\n)\n'; + expect(getSnippetHeaders(headersArray, ' ')).to.equal(expectedString); + }); + + it('should return an string representing the headers trim only values', function () { + const headersArray = + [ + { + key: 'my-sample-header ', + value: 'Lorem ipsum dolor sit amet ' + }, + { + key: 'testing', + value: '\'singlequotes\'' + }, + { + key: 'TEST', + value: '"doublequotes"' + } + ], + expectedString = 'headers = c(\n "my-sample-header" = "Lorem ipsum dolor sit amet ",\n' + + ' "testing" = "\'singlequotes\'",\n "TEST" = "\\"doublequotes\\""\n)\n'; + expect(getSnippetHeaders(headersArray, ' ')).to.equal(expectedString); + }); +}); + +describe('getSnippetPostFormInParams function', function () { + + it('should generate postForm snippet with params headers and post style', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', true, true); + expect(res).to.equal(expected); + }); + + it('should generate postForm snippet without params with headers and post style', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(httpheader = headers), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', false, true); + expect(res).to.equal(expected); + }); + + it('should generate postForm snippet without headers with params and post style', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', true, false); + expect(res).to.equal(expected); + }); + +}); + +describe('getSnippetGetURL function', function () { + + it('should generate get url snippet with headers', function () { + const expected = 'res <- getURL("https://postman-echo.com/headers", .opts=list(httpheader = headers))\n', + res = getSnippetGetURL('https://postman-echo.com/headers', true); + expect(res).to.equal(expected); + }); + + it('should generate postForm snippet without headers', function () { + const expected = 'res <- getURL("https://postman-echo.com/headers")\n', + res = getSnippetGetURL('https://postman-echo.com/headers', false); + expect(res).to.equal(expected); + }); + + it('should generate get url snippet with headers and timeout', function () { + const expected = 'res <- getURL("https://postman-echo.com/headers",' + + ' .opts=list(httpheader = headers, timeout.ms = 5000))\n', + res = getSnippetGetURL('https://postman-echo.com/headers', true, 5000); + expect(res).to.equal(expected); + }); + + it('should generate get url snippet without headers and with timeout', function () { + const expected = 'res <- getURL("https://postman-echo.com/headers",' + + ' .opts=list(timeout.ms = 5000))\n', + res = getSnippetGetURL('https://postman-echo.com/headers', false, 5000); + expect(res).to.equal(expected); + }); +}); + +describe('getSnippetRequest function', function () { + + it('should generate snippet method GET with headers', function () { + const expected = 'res <- getURL("https://postman-echo.com/headers", .opts=list(httpheader = headers))\n', + res = getSnippetRequest('https://postman-echo.com/headers', 'GET', '', false, true); + expect(res).to.equal(expected); + }); + + it('should generate snippet method GET with follow location in false', function () { + const expected = 'res <- getURL("https://postman-echo.com/headers")\n', + res = getSnippetRequest('https://postman-echo.com/headers', 'GET', '', false, false, + undefined, undefined, 0, false); + expect(res).to.equal(expected); + }); + + it('should generate snippet method GET without headers', function () { + const expected = 'res <- getURL("https://postman-echo.com/headers")\n', + res = getSnippetRequest('https://postman-echo.com/headers', 'GET', '', false, false); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST for url encoded with headers and params', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers), style = "post")\n', + res = getSnippetRequest('https://postman-echo.com/post', 'POST', 'post', + true, true, 'application/x-www-form-urlencoded', {}); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST for form data encoded with headers and params', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers), style = "post")\n', + res = getSnippetRequest('https://postman-echo.com/post', 'POST', 'post', + true, true, 'multipart/form-data', {}); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST for form data encoded with params and not follow location', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, style = "post")\n', + res = getSnippetRequest('https://postman-echo.com/post', 'POST', 'post', + true, false, 'multipart/form-data', {}, 0, false); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST for raw data', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(postfields = params, httpheader = headers), style = "post")\n', + res = getSnippetRequest('https://postman-echo.com/post', 'POST', 'post', + true, true, 'application/json', {}); + expect(res).to.equal(expected); + }); + +}); + +describe('getSnippetPostFormInParams method', function () { + + it('should generate snippet method POST with params headers and post style', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', true, true); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST with params headers and httppost style', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers), style = "httpost")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'httpost', true, true); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST with params headers, post style and timeout', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers, timeout.ms = 5000), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', true, true, 5000); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST with params without headers, post style and timeout', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(timeout.ms = 5000), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', true, false, 5000); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST with params no headers, post style timeout no follow location', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(timeout.ms = 5000), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', true, false, 5000, false); + expect(res).to.equal(expected); + }); +}); + +describe('getSnippetPostFormInParams method', function () { + it('should generate snippet method POST with params, headers and post style', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', true, true); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST with params, headers and httppost style', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers), style = "httpost")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'httpost', true, true); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST with params, headers post style and timeout', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .params = params, .opts=list(httpheader = headers, timeout.ms = 5000), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', true, true, 5000); + expect(res).to.equal(expected); + }); + + it('should generate snippet method POST without params, with headers post style', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(httpheader = headers), style = "post")\n', + res = getSnippetPostFormInParams('https://postman-echo.com/post', 'post', false, true, 0); + expect(res).to.equal(expected); + }); + +}); + +describe('getSnippetPostFormInOptions method', function () { + it('should return snippet with params and headers', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(postfields = params, httpheader = headers), style = "post")\n', + res = getSnippetPostFormInOptions('https://postman-echo.com/post', 'post', true, true); + expect(res).to.equal(expected); + }); + it('should return snippet with params and without headers', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(postfields = params), style = "post")\n', + res = getSnippetPostFormInOptions('https://postman-echo.com/post', 'post', true, false); + expect(res).to.equal(expected); + }); + it('should return snippet without params and with headers', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(httpheader = headers), style = "post")\n', + res = getSnippetPostFormInOptions('https://postman-echo.com/post', 'post', false, true); + expect(res).to.equal(expected); + }); + it('should return snippet without params and without headers', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' style = "post")\n', + res = getSnippetPostFormInOptions('https://postman-echo.com/post', 'post', false, false); + expect(res).to.equal(expected); + }); + it('should return snippet with params, headers and timeout', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(postfields = params, httpheader = headers, timeout.ms = 5000), style = "post")\n', + res = getSnippetPostFormInOptions('https://postman-echo.com/post', 'post', true, true, 5000); + expect(res).to.equal(expected); + }); + it('should return snippet without params and with headers and timeout', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(httpheader = headers, timeout.ms = 5000), style = "post")\n', + res = getSnippetPostFormInOptions('https://postman-echo.com/post', 'post', false, true, 5000); + expect(res).to.equal(expected); + }); + + it('should return snippet without params, headers and with timeout', function () { + const expected = 'res <- postForm("https://postman-echo.com/post",' + + ' .opts=list(timeout.ms = 5000), style = "post")\n', + res = getSnippetPostFormInOptions('https://postman-echo.com/post', 'post', false, false, 5000); + expect(res).to.equal(expected); + }); +}); + +describe('addContentTypeHeader method', function () { + it('should add content type header when is graphql', function () { + const request = new Request({ + 'method': 'POST', + 'header': [ + ], + 'url': { + 'raw': 'https://google.com', + 'protocol': 'https', + 'host': [ + 'google', + 'com' + ] + }, + 'body': { + mode: 'graphql' + } + }); + addContentTypeHeader(request); + expect(request.headers.members[0].value).to.equal('application/json'); + }); + + it('should not add content type header when is not graphql', function () { + const request = new Request({ + 'method': 'POST', + 'header': [ + ], + 'url': { + 'raw': 'https://google.com', + 'protocol': 'https', + 'host': [ + 'google', + 'com' + ] + }, + 'body': { + mode: 'raw' + } + }); + addContentTypeHeader(request); + expect(request.headers.members).to.be.empty; + }); +}); + +describe('buildOptionsSnippet method', function () { + it('should return options for params headers and timeout', function () { + const result = buildOptionsSnippet(true, true, 5000); + expect(result).to.equal('postfields = params, httpheader = headers, timeout.ms = 5000'); + }); + + it('should return options for params and headers', function () { + const result = buildOptionsSnippet(true, true, 0); + expect(result).to.equal('postfields = params, httpheader = headers'); + }); + + it('should return options for params and timeout', function () { + const result = buildOptionsSnippet(true, false, 5000); + expect(result).to.equal('postfields = params, timeout.ms = 5000'); + }); + + it('should return options for headers and timeout', function () { + const result = buildOptionsSnippet(false, true, 5000); + expect(result).to.equal('httpheader = headers, timeout.ms = 5000'); + }); + it('should return options for headers', function () { + const result = buildOptionsSnippet(false, true, 0); + expect(result).to.equal('httpheader = headers'); + }); + + it('should return options for timeout', function () { + const result = buildOptionsSnippet(false, false, 5000); + expect(result).to.equal('timeout.ms = 5000'); + }); + + it('should return empty string options for no options', function () { + const result = buildOptionsSnippet(false, false, 0); + expect(result).to.equal(''); + }); + + it('should return options for params headers timeout and follow redirect on false', function () { + const result = buildOptionsSnippet(true, true, 5000, false); + expect(result).to.equal('postfields = params, httpheader = headers, timeout.ms = 5000'); + }); + + it('should return options for headers timeout and follow redirect on false', function () { + const result = buildOptionsSnippet(false, true, 5000, false); + expect(result).to.equal('httpheader = headers, timeout.ms = 5000'); + }); + +}); + +describe('groupHeadersSameKey method', function () { + it('should group two headers with same key', function () { + const result = groupHeadersSameKey([{ key: 'key1', value: 'value1'}, { key: 'key1', value: 'value2'}]); + expect(result.length).to.equal(1); + expect(result[0].value).to.equal('value1, value2'); + expect(result[0].key).to.equal('key1'); + }); +}); + +describe('getIndentation function', function () { + it('should return 3 whitespaces when indentType is whitespace and indentCount is 3', function () { + expect(getIndentation({ indentType: ' ', indentCount: 3 })).to.equal(' '); + }); + + it('should return 3 spaces when indentType is the word Space and indentCount is 3', function () { + expect(getIndentation({ indentType: 'Space', indentCount: 3 })).to.equal(' '); + }); + + it('should return 3 tabspaces when indentType is the word Space and indentCount is 3', function () { + expect(getIndentation({ indentType: 'Space', indentCount: 3 })).to.equal(' '); + }); + + it('should return 1 tabspace when indentType is the word Tab and indentCount is 1', function () { + expect(getIndentation({ indentType: 'Tab', indentCount: 1 })).to.equal('\t'); + }); + + it('should return 2 whitespaces when there is no options object', function () { + expect(getIndentation()).to.equal(' '); + }); + + it('should return 2 whitespaces when there is no indentation options in object', function () { + expect(getIndentation({})).to.equal(' '); + }); +}); + +describe('getSnippetPut method', function () { + it('should return put snippet with params headers and follow location false', function () { + const result = getSnippetPut('url', true, true, 0, false); + expect(result).to.equal('res <- httpPUT("url", params, httpheader = headers)\n'); + }); + it('should return put snippet without params headers and follow location false', function () { + const result = getSnippetPut('url', false, true, 0, false); + expect(result).to.equal('res <- httpPUT("url", httpheader = headers)\n'); + }); + it('should return put snippet with params and no options', function () { + const result = getSnippetPut('url', true, false, 0, true); + expect(result).to.equal('res <- httpPUT("url", params, followlocation = TRUE)\n'); + }); + it('should return put snippet without params and no options', function () { + const result = getSnippetPut('url', false, false, 0, true); + expect(result).to.equal('res <- httpPUT("url", followlocation = TRUE)\n'); + }); +}); + +describe('getSnippetDelete method', function () { + it('should return delete snippet with params headers and follow location false', function () { + const result = getSnippetDelete('url', true, true, 0, false); + expect(result).to.equal( + 'res <- httpDELETE("url", postfields = params, httpheader = headers)\n'); + }); + it('should return delete snippet withoutout params and options', function () { + const result = getSnippetDelete('url', false, false, 0, true); + expect(result).to.equal('res <- httpDELETE("url", followlocation = TRUE)\n'); + }); +}); + +describe('getSnippetURLContent method', function () { + it('should return url content snippet for PATCH with params headers and follow location false', function () { + const result = getSnippetURLContent('url', true, true, 0, false, 'PATCH'); + expect(result).to.equal( + 'res <- getURLContent("url", customrequest = "PATCH", postfields = params, ' + + 'httpheader = headers)\n'); + }); + + it('should return delete snippet for PATCH withoutout params and options', function () { + const result = getSnippetURLContent('url', false, false, 0, true, 'PATCH'); + expect(result).to.equal('res <- getURLContent("url", customrequest = "PATCH", followlocation = TRUE)\n'); + }); +}); + diff --git a/codegens/ruby/.gitignore b/codegens/ruby/.gitignore index 85f712d79..ff2ebb52b 100644 --- a/codegens/ruby/.gitignore +++ b/codegens/ruby/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/ruby/lib/util/parseBody.js b/codegens/ruby/lib/util/parseBody.js index eafd8b9db..31ebd2f1f 100644 --- a/codegens/ruby/lib/util/parseBody.js +++ b/codegens/ruby/lib/util/parseBody.js @@ -43,8 +43,8 @@ module.exports = function (request, trimRequestBody, contentType, indentCount) { if (contentType && (contentType === 'application/json' || contentType.match(/\+json$/))) { try { let jsonBody = JSON.parse(request.body[request.body.mode]); - jsonBody = JSON.stringify(jsonBody, replacer, indentCount) - .replace(new RegExp(`"${nullToken}"`, 'g'), 'nil'); + jsonBody = sanitize(JSON.stringify(jsonBody, replacer, indentCount)); + jsonBody = jsonBody.replace(new RegExp(`"${nullToken}"`, 'g'), 'nil'); return `request.body = JSON.dump(${jsonBody})\n`; } catch (error) { diff --git a/codegens/ruby/lib/util/sanitize.js b/codegens/ruby/lib/util/sanitize.js index 1cc893054..9efe53fc7 100644 --- a/codegens/ruby/lib/util/sanitize.js +++ b/codegens/ruby/lib/util/sanitize.js @@ -13,6 +13,11 @@ module.exports = { return ''; } inputString = inputTrim && typeof inputTrim === 'boolean' ? inputString.trim() : inputString; + inputString = inputString + .replace(/`/g, '\\`') + .replace(/#/g, '\\#') + .replace(/\$/g, '\\$') + .replace(/!/g, '\\!'); if (escapeCharFor && typeof escapeCharFor === 'string') { switch (escapeCharFor) { case 'raw': diff --git a/codegens/ruby/test/newman/newman.test.js b/codegens/ruby/test/newman/newman.test.js index dce109836..cbb8c68f8 100644 --- a/codegens/ruby/test/newman/newman.test.js +++ b/codegens/ruby/test/newman/newman.test.js @@ -10,7 +10,7 @@ describe('Convert for different types of request', function () { fileName: 'codesnippet.rb', runScript: 'ruby codesnippet.rb', compileScript: null, - skipCollections: ['redirectCollection'] + skipCollections: ['redirectCollection', 'unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); diff --git a/codegens/ruby/test/unit/converter.test.js b/codegens/ruby/test/unit/converter.test.js index bad80c77e..f16626ac0 100644 --- a/codegens/ruby/test/unit/converter.test.js +++ b/codegens/ruby/test/unit/converter.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), convert = require('../../lib/index').convert; describe('Ruby converter', function () { @@ -9,7 +9,7 @@ describe('Ruby converter', function () { }); it('should set read_timeout when requestTimeout is set to non zero value', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -32,7 +32,7 @@ describe('Ruby converter', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -58,8 +58,41 @@ describe('Ruby converter', function () { }); }); + it('should escape special characters inside double quotes', function () { + var request = new Request({ + 'method': 'POST', + 'header': [ + 'Content-Type: application/json' + ], + 'body': { + 'mode': 'raw', + 'raw': '{\r\n "hi": "#{`curl https://postman-echo.com`}",\r\n "message": "This is a ruby Code"\r\n}', + 'options': { + 'raw': { + 'language': 'json' + } + } + }, + 'url': { + 'raw': 'https://google.com', + 'protocol': 'https', + 'host': [ + 'google', + 'com' + ] + } + }); + convert(request, {}, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('\\#{\\`curl https://postman-echo.com\\`}'); + }); + }); + it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { diff --git a/codegens/ruby/test/unit/fixtures/sample_collection.json b/codegens/ruby/test/unit/fixtures/sample_collection.json index 2cf55a2f6..ddcb46d36 100644 --- a/codegens/ruby/test/unit/fixtures/sample_collection.json +++ b/codegens/ruby/test/unit/fixtures/sample_collection.json @@ -1152,11 +1152,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1181,11 +1181,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1210,11 +1210,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1236,11 +1236,11 @@ ], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1265,11 +1265,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1294,11 +1294,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1315,13 +1315,11 @@ "header": [], "body": {}, "url": { - "raw": "https://8b32a2c6-4296-4c28-b383-889571041e69.mock.pstmn.io/testpurge", + "raw": "https://postman-echo.com/testpurge", "protocol": "https", "host": [ - "8b32a2c6-4296-4c28-b383-889571041e69", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path":[ "testpurge" @@ -1338,13 +1336,11 @@ "header": [], "body": {}, "url": { - "raw": "https://8cf6f433-4274-41e1-a763-cf45262ecd57.mock.pstmn.io/testcopy", + "raw": "https://postman-echo.com/testcopy", "protocol": "https", "host": [ - "8cf6f433-4274-41e1-a763-cf45262ecd57", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ], "path":[ "testcopy" @@ -1361,13 +1357,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, diff --git a/codegens/rust-reqwest/.gitignore b/codegens/rust-reqwest/.gitignore new file mode 100644 index 000000000..b2797fd43 --- /dev/null +++ b/codegens/rust-reqwest/.gitignore @@ -0,0 +1,78 @@ +.DS_Store +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Prevent IDE stuff +.idea +.vscode +*.sublime-* + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +.coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +out/ + +# Cargo +target/ +src/ +Cargo.toml +Cargo.lock \ No newline at end of file diff --git a/codegens/rust-reqwest/README.md b/codegens/rust-reqwest/README.md new file mode 100644 index 000000000..119951c19 --- /dev/null +++ b/codegens/rust-reqwest/README.md @@ -0,0 +1,79 @@ +# Code-Gen: Postman SDK Request -> Rust Snippet Converter + +This module is used to convert Postman SDK-Request object into a Rust code snippet. + +#### Prerequisites +To run this repository, ensure that you have NodeJS >= v12. + +## Using the Module +This module exposes two function `convert()` and `getOptions()` + +### Convert + +Convert function sanitizes the inputs, overrides options with the default ones if not provided and return the code snippet in the desired format. + +It requires 3 mandatory parameters `request`, `callback` and `options` + +* `request` - postman SDK-request object + +* `options` is an object with the following properties + + * `indentType` : can be `Tab` or `Space` (default: 'Space') + * `indentCount` : Integer denoting the number of tabs/spaces required for indentation, range 0-8 (default : for indentType Tab : 2, for indentType Space : 4) + * `trimRequestBody` : Trim request body fields (default: false) + These plugin options will be used if no options are passed to the convert function. + +* `callback` : callback function with `error` and `snippet` parameters where snippet is the desired output + +#### Example +```javascript +const sdk = require('postman-collection'); + +const request = sdk.Request('http://www.google.com'), + options = {indentType: 'Tab', indentCount: 4, trimRequestBody: true}; + +convert(request, options, function (err, snippet) { + if (err) { + // perform desired action of logging the error + } + // perform action with the snippet +}); +``` + +### getOptions function + +This function returns a list of options supported by this codegen. + +#### Example +```js +const options = getOptions(); + +console.log(options); +// output +// [ +// { +// name: 'Set indentation count', +// id: 'indentCount', +// type: 'positiveInteger', +// default: 2, +// description: 'Set the number of indentation characters to add per code level' +// }, +// ... +// ] +``` + +### Notes + +* This module supports all request types of requests which are present in the Postman App. + +* Does not handle cookies or proxies and generates a snippet for the base request. + +* Snippet generated is supported for Rust 1.0+ versions. + +* Does not support if the request body is passed by means of a binary file. + +* User â—Šneeds to enter the absolute path of the file in the snippet. This just picks the relative path in case of file upload in form data body. + +### Resources + +* [Rust](https://www.rust-lang.org/) official documentation diff --git a/codegens/rust-reqwest/index.js b/codegens/rust-reqwest/index.js new file mode 100644 index 000000000..bb0a047c4 --- /dev/null +++ b/codegens/rust-reqwest/index.js @@ -0,0 +1 @@ +module.exports = require('./lib'); diff --git a/codegens/rust-reqwest/lib/index.js b/codegens/rust-reqwest/lib/index.js new file mode 100644 index 000000000..1d1a7b6c7 --- /dev/null +++ b/codegens/rust-reqwest/lib/index.js @@ -0,0 +1,4 @@ +module.exports = { + convert: require('./reqwest').convert, + getOptions: require('./reqwest').getOptions +}; diff --git a/codegens/rust-reqwest/lib/reqwest.js b/codegens/rust-reqwest/lib/reqwest.js new file mode 100644 index 000000000..473610324 --- /dev/null +++ b/codegens/rust-reqwest/lib/reqwest.js @@ -0,0 +1,147 @@ +const _ = require('lodash'), + sanitizeOptions = require('./util/sanitize').sanitizeOptions, + { parseHeader, parseBody } = require('./util/parseRequest'), + { addDefaultContentType, formatFormData } = require('./util/formatRequest'), + ALLOWED_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'PATH', 'TRACE']; + +/** + * Returns snippet for Rust reqwest by parsing data from Postman-SDK request object + * + * @param {Object} request - Postman SDK request object + * @param {String} indentation - indentation required for code snippet + * @param {Object} options - Options for code generation + * + * @returns {String} - Rust reqwest code snippet for given request object + */ +function makeSnippet (request, indentation, options) { + // Build the client - set timeout and redirect policy + let snippet = '#[tokio::main]\n'; + snippet += 'async fn main() -> Result<(), Box> {\n'; + snippet += `${indentation}let client = reqwest::Client::builder()\n`; + + // Disable redirects if option is set + if (_.get(request, 'protocolProfileBehavior.followRedirects', options.followRedirect) === false) { + snippet += `${indentation.repeat(2)}.redirect(reqwest::redirect::Policy::none())\n`; + } + + snippet += `${indentation.repeat(2)}.build()?;\n\n`; + + addDefaultContentType(request); + request.body && request.body.mode === 'formdata' && formatFormData(request); + + const body = request.body && request.body.toJSON(), + contentType = request.headers.get('Content-Type'), + { headerSnippet, requestHeaderSnippet } = parseHeader(request, indentation), + { bodySnippet, requestBodySnippet } = parseBody(body, options.trimRequestBody, indentation, contentType); + + snippet += headerSnippet; + snippet += bodySnippet; + + // Use short method name if possible + let requestSnippet = ''; + if (ALLOWED_METHODS.includes(request.method)) { + requestSnippet += `${indentation}let request = client.request(reqwest::Method::${request.method}, `; + } + else { + requestSnippet += `${indentation}let method = "${request.method}";\n`; + requestSnippet += `${indentation}let request = client.request(reqwest::Method::from_bytes(method.as_bytes())?, `; + } + + // Add headers and body + requestSnippet += `"${request.url.toString()}")\n`; + requestSnippet += requestHeaderSnippet; + requestSnippet += requestBodySnippet; + + // Set request timeout + if (options.requestTimeout !== 0) { + requestSnippet += `${indentation.repeat(2)}.timeout(std::time::Duration::from_millis(${options.requestTimeout}))\n`; + } + + requestSnippet = requestSnippet.slice(0, -1) + ';\n\n'; + + snippet += requestSnippet; + snippet += `${indentation}let response = request.send().await?;\n`; + snippet += `${indentation}let body = response.text().await?;\n\n`; + + snippet += `${indentation}println!("{}", body);\n\n`; + snippet += `${indentation}Ok(())\n}`; + + return snippet; +} + +const self = module.exports = { + /** + * Used to return options which are specific to a particular plugin + * + * @returns {Array} + */ + getOptions: function () { + return [ + { + name: 'Set indentation count', + id: 'indentCount', + type: 'positiveInteger', + default: 4, + description: 'Set the number of indentation characters to add per code level' + }, + { + name: 'Set indentation type', + id: 'indentType', + type: 'enum', + availableOptions: ['Tab', 'Space'], + default: 'Space', + description: 'Select the character used to indent lines of code' + }, + { + name: 'Set request timeout', + id: 'requestTimeout', + type: 'positiveInteger', + default: 0, + description: 'Set number of milliseconds the request should wait for a response' + + ' before timing out (use 0 for infinity)' + }, + { + name: 'Follow redirects', + id: 'followRedirect', + type: 'boolean', + default: true, + description: 'Automatically follow HTTP redirects' + }, + { + name: 'Trim request body fields', + id: 'trimRequestBody', + type: 'boolean', + default: false, + description: 'Remove white space and additional lines that may affect the server\'s response' + }]; + }, + + /** + * Used to convert the postman sdk-request object to rust snippet + * + * @param {Object} request - postman SDK-request object + * @param {Object} options + * @param {String} options.indentType - type of indentation eg: Space / Tab (default: Space) + * @param {Number} options.indentCount - frequency of indent (default: 4 for indentType: Space, + default: 1 for indentType: Tab) + * @param {Number} options.requestTimeout : time in milli-seconds after which request will bail out + (default: 0 -> never bail out) + * @param {Boolean} options.trimRequestBody : whether to trim request body fields (default: false) + * @param {Boolean} options.followRedirect : whether to allow redirects of a request + * @param {Function} callback - function with parameters (error, snippet) + */ + convert: function (request, options, callback) { + if (!_.isFunction(callback)) { + throw new Error('Rust~reqwest-convert: Callback is not a function'); + } + options = sanitizeOptions(options, self.getOptions()); + + // String representing value of indentation required + let indentString; + + indentString = options.indentType === 'Tab' ? '\t' : ' '; + indentString = indentString.repeat(options.indentCount); + + return callback(null, makeSnippet(request, indentString, options)); + } +}; diff --git a/codegens/rust-reqwest/lib/util/formatRequest.js b/codegens/rust-reqwest/lib/util/formatRequest.js new file mode 100644 index 000000000..c53b42d61 --- /dev/null +++ b/codegens/rust-reqwest/lib/util/formatRequest.js @@ -0,0 +1,104 @@ +/** + * + * @param {Array} array - form data array + * @param {String} key - key of form data param + * @param {String} type - type of form data param(file/text) + * @param {String} val - value/src property of form data param + * @param {String} disabled - Boolean denoting whether the param is disabled or not + * @param {String} contentType - content type header of the param + * + * Appends a single param to form data array + */ +function addFormParam (array, key, type, val, disabled, contentType) { + if (type === 'file') { + array.push({ + key: key, + type: type, + src: val, + disabled: disabled, + contentType: contentType + }); + } + else { + array.push({ + key: key, + type: type, + value: val, + disabled: disabled, + contentType: contentType + }); + } +} + +module.exports = { + /** + * Adds default content-type header if not present in request + * + * @param {Object} request - Postman SDK request object + */ + addDefaultContentType: function (request) { + if (!request.body || request.headers.has('Content-Type')) { + return; + } + + if (request.body.mode === 'file') { + request.addHeader({ + key: 'Content-Type', + value: 'text/plain' + }); + } + else if (request.body.mode === 'graphql') { + request.addHeader({ + key: 'Content-Type', + value: 'application/json' + }); + } + }, + + /** + * The following code handles multiple files in the same formdata param. + * It removes the form data params where the src property is an array of filepath strings + * Splits that array into different form data params with src set as a single filepath string + * + * @param {Object} request - Postman SDK Request object + */ + formatFormData: function (request) { + const formdata = request.body.formdata, + formdataArray = []; + formdata.members.forEach((param) => { + const key = param.key, + type = param.type, + disabled = param.disabled, + contentType = param.contentType; + // check if type is file or text + if (type === 'file') { + // if src is not of type string we check for array(multiple files) + if (typeof param.src !== 'string') { + // if src is an array(not empty), iterate over it and add files as separate form fields + if (Array.isArray(param.src) && param.src.length) { + param.src.forEach((filePath) => { + addFormParam(formdataArray, key, param.type, filePath, disabled, contentType); + }); + } + // if src is not an array or string, or is an empty array, add a placeholder for file path(no files case) + else { + addFormParam(formdataArray, key, param.type, '/path/to/file', disabled, contentType); + } + } + // if src is string, directly add the param with src as filepath + else { + addFormParam(formdataArray, key, param.type, param.src, disabled, contentType); + } + } + // if type is text, directly add it to formdata array + else { + addFormParam(formdataArray, key, param.type, param.value, disabled, contentType); + } + }); + + request.body.update({ + mode: 'formdata', + formdata: formdataArray + }); + } +}; diff --git a/codegens/rust-reqwest/lib/util/parseRequest.js b/codegens/rust-reqwest/lib/util/parseRequest.js new file mode 100644 index 000000000..e8dbcf810 --- /dev/null +++ b/codegens/rust-reqwest/lib/util/parseRequest.js @@ -0,0 +1,245 @@ +const _ = require('lodash'), + path = require('path'), + { sanitize } = require('./sanitize'); + +/** + * Parses header of request object and returns code snippet to add headers + * + * @param {Object} request - Postman SDK request object + * @param {String} indentation - indentation required in code snippet + * + * @typedef {Object} HeaderSnippet - code snippet to add headers to Rust reqwest + * @property {String} headerSnippet - code snippet to define headers + * @property {String} requestHeaderSnippet - code snippet to add headers to request + * + * @returns {HeaderSnippet} + */ +function parseHeader (request, indentation) { + const enabledHeaders = request.getHeaders({ enabled: true }); + let headerSnippet = '', + requestHeaderSnippet = ''; + + if (_.isEmpty(enabledHeaders)) { + return { headerSnippet, requestHeaderSnippet }; + } + + headerSnippet += `${indentation}let mut headers = reqwest::header::HeaderMap::new();\n`; + + _.forEach(enabledHeaders, (value, key) => { + if (Array.isArray(value)) { + value = value.join(', '); + } + + headerSnippet += `${indentation}headers.insert("${sanitize(key)}", `; + headerSnippet += `"${sanitize(value)}".parse()?);\n`; + }); + + headerSnippet += '\n'; + requestHeaderSnippet += `${indentation.repeat(2)}.headers(headers)\n`; + + return { headerSnippet, requestHeaderSnippet }; +} + +/** + * Parses URLEncoded body to Rust reqwest syntax + * + * @param {Object} body URLEncoded Body + * @param {boolean} trim trim body option + * @param {string} indentation The indentation string + * + * @returns {BodySnippet} + */ +function parseURLEncodedBody (body, trim, indentation) { + let bodySnippet = `${indentation}let mut params = std::collections::HashMap::new();\n`, + requestBodySnippet = `${indentation.repeat(2)}.form(¶ms)\n`; + + _.forEach(body, function (data) { + if (!data.disabled) { + bodySnippet += `${indentation}params.insert("${sanitize(data.key, trim)}", `; + bodySnippet += `"${sanitize(data.value, trim)}");\n`; + } + }); + + bodySnippet += '\n'; + + return { bodySnippet, requestBodySnippet }; +} + +/** + * Parses raw body to Rust reqwest syntax + * + * @param {Object} body Raw body + * @param {Boolean} trim trim body option + * @param {String} indentation The indentation string + * @param {String} contentType Content type of the body being sent + * + * @returns {BodySnippet} + */ +function parseRawBody (body, trim, indentation, contentType) { + let bodySnippet = '', + requestBodySnippet = ''; + + // Check if the body is a valid JSON + // If it is, then parse it to serde_json::Value + // Else, just send it as a string + if (contentType && contentType.startsWith('application/json')) { + try { + const jsonBody = JSON.parse(body), + jsonValue = JSON.stringify(jsonBody, null, indentation); + bodySnippet += `${indentation}let data = r#"${jsonValue}"#;\n\n`; + bodySnippet += `${indentation}let json: serde_json::Value = serde_json::from_str(&data)?;\n\n`; + requestBodySnippet += `${indentation.repeat(2)}.json(&json)\n`; + } + catch (e) { + bodySnippet = `${indentation}let data = "${sanitize(body, trim)}";\n\n`; + requestBodySnippet += `${indentation.repeat(2)}.body(data)\n`; + } + } + else { + bodySnippet = `${indentation}let data = "${sanitize(body, trim)}";\n\n`; + requestBodySnippet += `${indentation.repeat(2)}.body(data)\n`; + } + + return { bodySnippet, requestBodySnippet }; +} + +/** + * Parses GraphQL body to Rust reqwest syntax + * + * @param {Object} body Raw body + * @param {Boolean} trim trim body option + * @param {String} indentation The indentation string + * + * @returns {BodySnippet} + */ +function parseGraphQL (body, trim, indentation) { + let query = body ? sanitize(body.query, trim) : '', + bodySnippet = '', + requestBodySnippet = ''; + + bodySnippet += `${indentation}let data = r#"\n{\n${indentation}"query": "${query}"`; + if (body && body.variables) { + const variables = trim ? body.variables.trim() : body.variables; + bodySnippet += `,\n${indentation}"variables": ${variables}`; + } + bodySnippet += '\n}\n"#;\n'; + bodySnippet += `${indentation}let json: serde_json::Value = serde_json::from_str(&data)?;\n\n`; + requestBodySnippet += `${indentation.repeat(2)}.json(&json)\n`; + + return { bodySnippet, requestBodySnippet }; +} + +/** + * Parses Formdata to Rust reqwest syntax + * + * @param {Object} body FormData body + * @param {Boolean} trim trim body option + * @param {String} indentation The indentation string + */ +function parseFormData (body, trim, indentation) { + let beforeSnippet = '', + index = 1, // this is used to generate headers for each part + bodySnippet = `${indentation}let form = reqwest::multipart::Form::new()\n`, + requestBodySnippet = `${indentation.repeat(2)}.multipart(form)\n`; + + _.forEach(body, function (data) { + if (!data.disabled) { + if (data.type === 'file') { + + const filename = data.src.split(path.sep).pop(); + bodySnippet += `${indentation.repeat(2)}.part("${sanitize(data.key, trim)}", `; + bodySnippet += `reqwest::multipart::Part::bytes(std::fs::read("${sanitize(data.src, trim)}")?)`; + bodySnippet += `.file_name("${filename}")`; + + if (data.contentType) { + beforeSnippet += `${indentation}let mut form_param${index}_headers = `; + beforeSnippet += 'reqwest::header::HeaderMap::new();\n'; + beforeSnippet += `${indentation}form_param${index}_headers.insert("Content-Type", `; + beforeSnippet += `"${sanitize(data.contentType, trim)}".parse()?);\n\n`; + + bodySnippet += `.headers(form_param${index}_headers)`; + } + + bodySnippet += ')\n'; + + index++; + } + else if (data.contentType) { + + beforeSnippet += `${indentation}let mut form_param${index}_headers = `; + beforeSnippet += 'reqwest::header::HeaderMap::new();\n'; + beforeSnippet += `${indentation}form_param${index}_headers.insert("Content-Type", `; + beforeSnippet += `"${sanitize(data.contentType, trim)}".parse()?);\n\n`; + + bodySnippet += `${indentation.repeat(2)}.part("${sanitize(data.key, trim)}", `; + bodySnippet += `reqwest::multipart::Part::text("${sanitize(data.value, trim)}")`; + bodySnippet += `.headers(form_param${index}_headers))\n`; + + index++; + } + else { + bodySnippet += `${indentation.repeat(2)}.text("${sanitize(data.key, trim)}", `; + bodySnippet += `"${sanitize(data.value, trim)}")\n`; + } + } + }); + + bodySnippet = beforeSnippet + bodySnippet.slice(0, -1) + ';\n\n'; + + return { bodySnippet, requestBodySnippet }; +} + +/** + * Parses File body to Rust reqwest syntax + * + * @param {Object} body File body + * @param {String} indentation The indentation string + * + * @returns {BodySnippet} + */ +function parseFileData (body, indentation) { + const bodySnippet = `${indentation}let bytes = std::fs::read("${sanitize(body.src)}")?;\n\n`, + requestBodySnippet = `${indentation.repeat(2)}.body(bytes)\n`; + + return { bodySnippet, requestBodySnippet }; +} + +/** + * Parses Body from the Request and returns code snippet to add body + * + * @param {Object} body body object from request + * @param {Boolean} trim trim body option + * @param {String} indentation indentation to be added to the snippet + * @param {String} contentType Content type of the body being sent + * + * @typedef {Object} BodySnippet - code snippet to add body to Rust reqwest + * @property {String} bodySnippet - code snippet to define body + * @property {String} requestBodySnippet - code snippet to add body to request + * + * @returns {BodySnippet} + */ +function parseBody (body, trim, indentation, contentType) { + if (!body || _.isEmpty(body)) { + return { bodySnippet: '', requestBodySnippet: '' }; + } + + switch (body.mode) { + case 'urlencoded': + return parseURLEncodedBody(body.urlencoded, trim, indentation); + case 'raw': + return parseRawBody(body.raw, trim, indentation, contentType); + case 'graphql': + return parseGraphQL(body.graphql, trim, indentation); + case 'formdata': + return parseFormData(body.formdata, trim, indentation); + case 'file': + return parseFileData(body.file, indentation); + default: + return parseRawBody(body[body.mode], trim, indentation, contentType); + } +} + +module.exports = { + parseHeader, + parseBody +}; diff --git a/codegens/rust-reqwest/lib/util/sanitize.js b/codegens/rust-reqwest/lib/util/sanitize.js new file mode 100644 index 000000000..a047aad6c --- /dev/null +++ b/codegens/rust-reqwest/lib/util/sanitize.js @@ -0,0 +1,91 @@ +module.exports = { + /** + * sanitizes input string by handling escape characters eg: converts '''' to '\'\'' + * and trim input if required + * + * @param {String} inputString + * @param {Boolean} [trim] - indicates whether to trim string or not + * @returns {String} + */ + sanitize: function (inputString, trim) { + if (typeof inputString !== 'string') { + return ''; + } + + (trim) && (inputString = inputString.trim()); + return inputString + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t'); + }, + + /** + * sanitizes input options + * + * @param {Object} options - Options provided by the user + * @param {Array} optionsArray - options array received from getOptions function + * + * @returns {Object} - Sanitized options object + */ + sanitizeOptions: function (options, optionsArray) { + var result = {}, + defaultOptions = {}, + id; + optionsArray.forEach((option) => { + defaultOptions[option.id] = { + default: option.default, + type: option.type + }; + if (option.type === 'enum') { + defaultOptions[option.id].availableOptions = option.availableOptions; + } + }); + + for (id in options) { + if (options.hasOwnProperty(id)) { + if (defaultOptions[id] === undefined) { + continue; + } + switch (defaultOptions[id].type) { + case 'boolean': + if (typeof options[id] !== 'boolean') { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'positiveInteger': + if (typeof options[id] !== 'number' || options[id] < 0) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + case 'enum': + if (!defaultOptions[id].availableOptions.includes(options[id])) { + result[id] = defaultOptions[id].default; + } + else { + result[id] = options[id]; + } + break; + default: + result[id] = options[id]; + } + } + } + + for (id in defaultOptions) { + if (defaultOptions.hasOwnProperty(id)) { + if (result[id] === undefined) { + result[id] = defaultOptions[id].default; + } + } + } + return result; + } +}; diff --git a/codegens/rust-reqwest/npm/test-lint.js b/codegens/rust-reqwest/npm/test-lint.js new file mode 100644 index 000000000..2f4db0cb8 --- /dev/null +++ b/codegens/rust-reqwest/npm/test-lint.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +var shell = require('shelljs'), + chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/rust-reqwest/npm/test-newman.js b/codegens/rust-reqwest/npm/test-newman.js new file mode 100644 index 000000000..ae7d2afe1 --- /dev/null +++ b/codegens/rust-reqwest/npm/test-newman.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all newman tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'newman'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running newman tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/rust-reqwest/npm/test-unit.js b/codegens/rust-reqwest/npm/test-unit.js new file mode 100755 index 000000000..0de7fd529 --- /dev/null +++ b/codegens/rust-reqwest/npm/test-unit.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +/* eslint-env node, es6 */ +// --------------------------------------------------------------------------------------------------------------------- +// This script is intended to execute all unit tests. +// --------------------------------------------------------------------------------------------------------------------- + +var shell = require('shelljs'), + + // set directories and files for test and coverage report + path = require('path'), + + NYC = require('nyc'), + chalk = require('chalk'), + recursive = require('recursive-readdir'), + + COV_REPORT_PATH = '.coverage', + SPEC_SOURCE_DIR = path.join(__dirname, '..', 'test', 'unit'); + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('Running unit tests using mocha on node...')); + + shell.test('-d', COV_REPORT_PATH) && shell.rm('-rf', COV_REPORT_PATH); + shell.mkdir('-p', COV_REPORT_PATH); + + var Mocha = require('mocha'), + nyc = new NYC({ + reportDir: COV_REPORT_PATH, + tempDirectory: COV_REPORT_PATH, + reporter: ['text', 'lcov', 'text-summary'], + exclude: ['config', 'test'], + hookRunInContext: true, + hookRunInThisContext: true + }); + + nyc.wrap(); + // add all spec files to mocha + recursive(SPEC_SOURCE_DIR, function (err, files) { + if (err) { console.error(err); return exit(1); } + + var mocha = new Mocha({ timeout: 1000 * 60 }); + + files.filter(function (file) { // extract all test files + return (file.substr(-8) === '.test.js'); + }).forEach(mocha.addFile.bind(mocha)); + + mocha.run(function (runError) { + runError && console.error(runError.stack || runError); + + nyc.reset(); + nyc.writeCoverageFile(); + nyc.report(); + exit(runError ? 1 : 0); + }); + }); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/rust-reqwest/npm/test.js b/codegens/rust-reqwest/npm/test.js new file mode 100755 index 000000000..b20d0f308 --- /dev/null +++ b/codegens/rust-reqwest/npm/test.js @@ -0,0 +1,16 @@ +#!/usr/bin/env node +var chalk = require('chalk'), + exit = require('shelljs').exit, + prettyms = require('pretty-ms'), + startedAt = Date.now(), + name = require('../package.json').name; + +require('async').series([ + require('./test-lint'), + require('./test-newman'), + require('./test-unit') +], function (code) { + // eslint-disable-next-line max-len + console.info(chalk[code ? 'red' : 'green'](`\n${name}: duration ${prettyms(Date.now() - startedAt)}\n${name}: ${code ? 'not ok' : 'ok'}!`)); + exit(code && (typeof code === 'number' ? code : 1) || 0); +}); diff --git a/codegens/rust-reqwest/package.json b/codegens/rust-reqwest/package.json new file mode 100644 index 000000000..8b9eac3f4 --- /dev/null +++ b/codegens/rust-reqwest/package.json @@ -0,0 +1,36 @@ +{ + "name": "@postman/codegen-rust-reqwest", + "version": "0.0.1", + "description": "Generates code snippets for a postman collection using Rust's reqwest library", + "com_postman_plugin": { + "type": "code_generator", + "lang": "Rust", + "variant": "reqwest", + "syntax_mode": "rust" + }, + "main": "index.js", + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "node npm/test.js", + "test-lint": "node npm/test-lint.js", + "test-newman": "node npm/test-newman.js", + "test-unit": "node npm/test-unit.js" + }, + "repository": { + "type": "git", + "url": "" + }, + "author": "Postman Labs ", + "license": "Apache-2.0", + "homepage": "https://github.com/postmanlabs/code-generators/tree/master/codegens/rust-reqwest", + "dependencies": { + "lodash": "4.17.21" + }, + "devDependencies": {}, + "engines": { + "node": ">=12" + } +} diff --git a/codegens/rust-reqwest/test/.eslintrc b/codegens/rust-reqwest/test/.eslintrc new file mode 100644 index 000000000..9d477e31e --- /dev/null +++ b/codegens/rust-reqwest/test/.eslintrc @@ -0,0 +1,30 @@ +{ + "plugins": [ + "mocha" + ], + "env": { + "mocha": true, + "node": true, + "es6": true + }, + "rules": { + // Mocha + "mocha/handle-done-callback": "error", + "mocha/max-top-level-suites": "error", + "mocha/no-exclusive-tests": "error", + "mocha/no-global-tests": "error", + "mocha/no-hooks-for-single-case": "off", + "mocha/no-hooks": "off", + "mocha/no-identical-title": "error", + "mocha/no-mocha-arrows": "error", + "mocha/no-nested-tests": "error", + "mocha/no-pending-tests": "error", + "mocha/no-return-and-callback": "error", + "mocha/no-sibling-hooks": "error", + "mocha/no-skipped-tests": "warn", + "mocha/no-synchronous-tests": "off", + "mocha/no-top-level-hooks": "warn", + "mocha/valid-test-description": "off", + "mocha/valid-suite-description": "off" + } +} diff --git a/codegens/rust-reqwest/test/ci-install.sh b/codegens/rust-reqwest/test/ci-install.sh new file mode 100755 index 000000000..36397d70c --- /dev/null +++ b/codegens/rust-reqwest/test/ci-install.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing Rust" + sudo apt-get install -y build-essential pkg-config libssl-dev + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + pushd ./codegens/rust-reqwest &>/dev/null; + echo '''[package] + name = "rust_reqwest_codegen" + version = "0.0.1" + edition = "2021" + + [dependencies] + reqwest = { version = "0.11.14", features = ["json", "multipart"] } + tokio = { version = "1.26.0", features = ["full"] } + serde_json = { version = "1.0.94" }''' > Cargo.toml + mkdir src && echo '''fn main() { + println!("Hello, world!"); + }''' > src/main.rs + cargo build + popd &>/dev/null; diff --git a/codegens/rust-reqwest/test/newman/newman.test.js b/codegens/rust-reqwest/test/newman/newman.test.js new file mode 100644 index 000000000..377f97ba9 --- /dev/null +++ b/codegens/rust-reqwest/test/newman/newman.test.js @@ -0,0 +1,15 @@ +const runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, + convert = require('../../index').convert; + +describe('Convert for different types of request', function () { + const testConfig = { + runScript: 'cargo run -q', + compileScript: null, + fileName: 'src/main.rs' + }, + options = { + indentCount: 4, + indentType: 'Space' + }; + runNewmanTest(convert, options, testConfig); +}); diff --git a/codegens/rust-reqwest/test/unit/convert.test.js b/codegens/rust-reqwest/test/unit/convert.test.js new file mode 100644 index 000000000..176e12fd2 --- /dev/null +++ b/codegens/rust-reqwest/test/unit/convert.test.js @@ -0,0 +1,120 @@ +const expect = require('chai').expect, + { Request } = require('postman-collection/lib/collection/request'), + convert = require('../../lib/index').convert; + +describe('Rust reqwest converter', function () { + it('should throw an error when callback is not function', function () { + expect(function () { convert({}, {}); }) + .to.throw('Rust~reqwest-convert: Callback is not a function'); + }); + + it('should set no redirect policy when followRedirect is set to false', function () { + const request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'http://postman-echo.com/get', + 'protocol': 'http', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }), + options = {followRedirect: false}; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('redirect(reqwest::redirect::Policy::none())'); + }); + }); + + it('should set read timeout when requestTimeout is set to non zero value', function () { + const request = new Request({ + 'method': 'GET', + 'header': [], + 'url': { + 'raw': 'http://postman-echo.com/get', + 'protocol': 'http', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }), + options = {requestTimeout: 3000}; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include('timeout(std::time::Duration::from_millis(3000))'); + }); + }); + + it('should use the method name directly if it is part of allowed methods', function () { + ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'PATH', 'TRACE'].forEach(function (method) { + const request = new Request({ + 'method': method, + 'header': [], + 'url': { + 'raw': 'http://postman-echo.com/get', + 'protocol': 'http', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }), + options = {}; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include(`let request = client.request(reqwest::Method::${method}`); + }); + }); + }); + + it('should use the method name using bytes if it not part of the allowed list', function () { + ['PROPFIND', 'PURGE', 'LOCK', 'UNLOCK', 'LINK', 'UNLINK', 'COPY'].forEach(function (method) { + const request = new Request({ + 'method': method, + 'header': [], + 'url': { + 'raw': 'http://postman-echo.com/get', + 'protocol': 'http', + 'host': [ + 'postman-echo', + 'com' + ], + 'path': [ + 'get' + ] + } + }), + options = {}; + convert(request, options, function (error, snippet) { + if (error) { + expect.fail(null, null, error); + } + expect(snippet).to.be.a('string'); + expect(snippet).to.include(`let method = "${method}"`); + expect(snippet).to.include('let request = client.request(reqwest::Method::from_bytes(method.as_bytes())?'); + }); + }); + }); + +}); diff --git a/codegens/rust-reqwest/test/unit/validation.test.js b/codegens/rust-reqwest/test/unit/validation.test.js new file mode 100644 index 000000000..15782bebc --- /dev/null +++ b/codegens/rust-reqwest/test/unit/validation.test.js @@ -0,0 +1,30 @@ +var expect = require('chai').expect, + path = require('path'), + + package = require(path.resolve('.', 'package.json')); + + +describe('package.json', function () { + it('should have com_postman_plugin object with valid properties', function () { + expect(package).to.have.property('com_postman_plugin'); + + expect(package.com_postman_plugin.type).to.equal('code_generator'); + expect(package.com_postman_plugin.lang).to.be.a('string'); + expect(package.com_postman_plugin.variant).to.be.a('string'); + expect(package.com_postman_plugin.syntax_mode).to.be.equal('rust'); + }); + it('should have main property with relative path to object with convert property', function () { + var languageModule; + + expect(package.main).to.be.a('string'); + + try { + languageModule = require(path.resolve('.', package.main)); + } + catch (error) { + console.error(error); + } + expect(languageModule).to.be.a('object'); + expect(languageModule.convert).to.be.a('function'); + }); +}); diff --git a/codegens/shell-httpie/.gitignore b/codegens/shell-httpie/.gitignore index f860b0ae0..08b0dc346 100644 --- a/codegens/shell-httpie/.gitignore +++ b/codegens/shell-httpie/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/shell-httpie/examples/test-collection.json b/codegens/shell-httpie/examples/test-collection.json index b51bcfeed..71d5e28d0 100644 --- a/codegens/shell-httpie/examples/test-collection.json +++ b/codegens/shell-httpie/examples/test-collection.json @@ -1014,11 +1014,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1043,11 +1043,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1072,11 +1072,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1093,11 +1093,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1108,7 +1108,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1122,11 +1122,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1151,11 +1151,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1172,13 +1172,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] }, "description": null @@ -1192,13 +1190,11 @@ "header": [], "body": {}, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1287,7 +1283,7 @@ ], "cookie": [], "responseTime": "375", - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1298,11 +1294,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1319,11 +1315,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1435,14 +1431,14 @@ { "expires": "Thu Mar 14 2019 13:12:10 GMT+0530 (IST)", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/codegens/shell-httpie/npm/test-lint.js b/codegens/shell-httpie/npm/test-lint.js new file mode 100755 index 000000000..71f753b81 --- /dev/null +++ b/codegens/shell-httpie/npm/test-lint.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +var shell = require('shelljs'), + chalk = require('chalk'), + async = require('async'), + ESLintCLIEngine = require('eslint').CLIEngine, + + /** + * The list of source code files / directories to be linted. + * + * @type {Array} + */ + LINT_SOURCE_DIRS = [ + './lib', + './test', + './npm/*.js', + './index.js' + ]; + +module.exports = function (exit) { + // banner line + console.info(chalk.yellow.bold('\nLinting files using eslint...')); + + async.waterfall([ + + /** + * Instantiates an ESLint CLI engine and runs it in the scope defined within LINT_SOURCE_DIRS. + * + * @param {Function} next - The callback function whose invocation marks the end of the lint test run. + * @returns {*} + */ + function (next) { + next(null, (new ESLintCLIEngine()).executeOnFiles(LINT_SOURCE_DIRS)); + }, + + /** + * Processes a test report from the Lint test runner, and displays meaningful results. + * + * @param {Object} report - The overall test report for the current lint test. + * @param {Object} report.results - The set of test results for the current lint run. + * @param {Function} next - The callback whose invocation marks the completion of the post run tasks. + * @returns {*} + */ + function (report, next) { + var errorReport = ESLintCLIEngine.getErrorResults(report.results); + // log the result to CLI + console.info(ESLintCLIEngine.getFormatter()(report.results)); + // log the success of the parser if it has no errors + (errorReport && !errorReport.length) && console.info(chalk.green('eslint ok!')); + // ensure that the exit code is non zero in case there was an error + next(Number(errorReport && errorReport.length) || 0); + } + ], exit); +}; + +// ensure we run this script exports if this is a direct stdin.tty run +!module.parent && module.exports(shell.exit); diff --git a/codegens/shell-httpie/package-lock.json b/codegens/shell-httpie/package-lock.json deleted file mode 100644 index ae748f3e9..000000000 --- a/codegens/shell-httpie/package-lock.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "@postman/codegen-shell-httpie", - "version": "0.1.0", - "lockfileVersion": 1 -} diff --git a/codegens/shell-httpie/package.json b/codegens/shell-httpie/package.json index 1c772fcd6..da9f0769a 100644 --- a/codegens/shell-httpie/package.json +++ b/codegens/shell-httpie/package.json @@ -15,7 +15,7 @@ }, "scripts": { "test": "./scripts/test.sh", - "test-lint": "./scripts/test-lint.sh", + "test-lint": "node npm/test-lint.js", "test-unit": "./scripts/test-unit.sh", "coverage": "nyc report --reporter=html --reporter=text mocha" }, diff --git a/codegens/shell-httpie/scripts/test-lint.sh b/codegens/shell-httpie/scripts/test-lint.sh deleted file mode 100755 index b6879c156..000000000 --- a/codegens/shell-httpie/scripts/test-lint.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# ---------------------------------------------------------------------------------------------------------------------- -# This script is intended to contain all actions pertaining to code style checking, linting and normalisation. -# -# 1. The script executes linting routines on specific folders. -# ---------------------------------------------------------------------------------------------------------------------- - -# Stop on first error -set -e; - -# banner -echo -e "\033[93mLinting and style-checking...\033[0m"; -echo -en "\033[0m\033[2m"; -echo -e "eslint `eslint -v`\033[0m\n"; - -# run style checker -eslint ./lib/** ./test/** ; -echo -en "\033[92mNo lint errors found.\n\033[0m"; diff --git a/codegens/shell-httpie/test/ci-install.sh b/codegens/shell-httpie/test/ci-install.sh new file mode 100755 index 000000000..a22593a82 --- /dev/null +++ b/codegens/shell-httpie/test/ci-install.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/shell-httpie" +sudo apt-get install httpie diff --git a/codegens/shell-httpie/test/unit/converter.test.js b/codegens/shell-httpie/test/unit/converter.test.js index 6ac8a475b..3acce1c06 100644 --- a/codegens/shell-httpie/test/unit/converter.test.js +++ b/codegens/shell-httpie/test/unit/converter.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, convert = require('../../index').convert, sanitize = require('../../lib/util/sanitize').quote; @@ -18,7 +18,7 @@ describe('Shell-Httpie convert function', function () { }); it('should add a timeout of 1 hour (3600 seconds) for RequestTimeout set to 0', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -43,7 +43,7 @@ describe('Shell-Httpie convert function', function () { }); it('should add port in the url when host has port specified', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { @@ -69,7 +69,7 @@ describe('Shell-Httpie convert function', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -96,7 +96,7 @@ describe('Shell-Httpie convert function', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -145,7 +145,7 @@ describe('Shell-Httpie convert function', function () { }); it('should generate valid snippet and should include appropriate variable name', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'body': {}, diff --git a/codegens/shell-wget/.gitignore b/codegens/shell-wget/.gitignore index 2e17627c5..20ec5c9a3 100644 --- a/codegens/shell-wget/.gitignore +++ b/codegens/shell-wget/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid diff --git a/codegens/shell-wget/test/newman/newman.test.js b/codegens/shell-wget/test/newman/newman.test.js index e10eb7698..359425d66 100644 --- a/codegens/shell-wget/test/newman/newman.test.js +++ b/codegens/shell-wget/test/newman/newman.test.js @@ -8,7 +8,8 @@ describe('Convert for different types of request', function () { }, testConfig = { footerSnippet: ' -qO-', // Added this to get the response in stdout instead of saving in file. - skipCollections: ['formdataCollection', 'sameNameHeadersCollection', 'formdataFileCollection'] + skipCollections: ['formdataCollection', 'sameNameHeadersCollection', 'formdataFileCollection', + 'unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); }); diff --git a/codegens/shell-wget/test/unit/converter.test.js b/codegens/shell-wget/test/unit/converter.test.js index f9ad3ab37..55f855744 100644 --- a/codegens/shell-wget/test/unit/converter.test.js +++ b/codegens/shell-wget/test/unit/converter.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), sanitize = require('../../lib/util/sanitize').sanitize, convert = require('../../lib/index').convert, getOptions = require('../../lib/index').getOptions, @@ -9,7 +9,7 @@ describe('Shell-Wget converter', function () { describe('convert function', function () { - var request = new sdk.Request(mainCollection.item[0].request), + var request = new Request(mainCollection.item[0].request), snippetArray, options = { indentType: 'Tab', @@ -82,7 +82,7 @@ describe('Shell-Wget converter', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -110,7 +110,7 @@ describe('Shell-Wget converter', function () { }); it('should generate snippet for formdata body mode', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -153,7 +153,7 @@ describe('Shell-Wget converter', function () { }); it('should generate valid snippet for single/double quotes in url', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [], 'url': { diff --git a/codegens/shell-wget/test/unit/fixtures/sample_collection.json b/codegens/shell-wget/test/unit/fixtures/sample_collection.json index f7021038a..a83c5b001 100644 --- a/codegens/shell-wget/test/unit/fixtures/sample_collection.json +++ b/codegens/shell-wget/test/unit/fixtures/sample_collection.json @@ -1105,11 +1105,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1134,11 +1134,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1163,11 +1163,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1184,11 +1184,11 @@ "header": [], "body": {}, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1213,11 +1213,11 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" diff --git a/codegens/swift/.gitignore b/codegens/swift/.gitignore index 125ac9e1d..b6c6170b1 100644 --- a/codegens/swift/.gitignore +++ b/codegens/swift/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Runtime data pids *.pid @@ -66,3 +72,4 @@ typings/ snippet.swift out/ +swift-5.7.3-RELEASE-ubuntu20.04/ diff --git a/codegens/swift/lib/swift.js b/codegens/swift/lib/swift.js index b274109cb..a27f2fd8d 100644 --- a/codegens/swift/lib/swift.js +++ b/codegens/swift/lib/swift.js @@ -109,31 +109,36 @@ function parseFormData (body, mode, trim, indent) { } }); parameters = '[\n' + _.join(parameters, ',\n') + ']'; - bodySnippet = `let parameters = ${parameters} as [[String : Any]]\n\n`; + bodySnippet = `let parameters = ${parameters} as [[String: Any]]\n\n`; bodySnippet += 'let boundary = "Boundary-\\(UUID().uuidString)"\n'; - bodySnippet += 'var body = ""\nvar error: Error? = nil\n'; + bodySnippet += 'var body = Data()\nvar error: Error? = nil\n'; bodySnippet += 'for param in parameters {\n'; - bodySnippet += `${indent}if param["disabled"] == nil {\n`; - bodySnippet += `${indent.repeat(2)}let paramName = param["key"]!\n`; - bodySnippet += `${indent.repeat(2)}body += "--\\(boundary)\\r\\n"\n`; + bodySnippet += `${indent}if param["disabled"] != nil { continue }\n`; + bodySnippet += `${indent}let paramName = param["key"]!\n`; + bodySnippet += `${indent}body += Data("--\\(boundary)\\r\\n".utf8)\n`; // eslint-disable-next-line no-useless-escape - bodySnippet += `${indent.repeat(2)}body += "Content-Disposition:form-data; name=\\"\\(paramName)\\"\"\n`; - bodySnippet += `${indent.repeat(2)}if param["contentType"] != nil {\n`; - bodySnippet += `${indent.repeat(3)}body += "\\r\\nContent-Type: \\(param["contentType"] as! String)"\n`; + bodySnippet += `${indent}body += Data("Content-Disposition:form-data; name=\\"\\(paramName)\\"\".utf8)\n`; + bodySnippet += `${indent}if param["contentType"] != nil {\n`; + bodySnippet += `${indent.repeat(2)}body += Data("\\r\\nContent-Type: \\(param["contentType"] as! String)".utf8)\n`; + bodySnippet += `${indent}}\n`; + bodySnippet += `${indent}let paramType = param["type"] as! String\n`; + bodySnippet += `${indent}if paramType == "text" {\n`; + bodySnippet += `${indent.repeat(2)}let paramValue = param["value"] as! String\n`; + bodySnippet += `${indent.repeat(2)}body += Data("\\r\\n\\r\\n\\(paramValue)\\r\\n".utf8)\n`; + bodySnippet += `${indent}} else {\n`; + bodySnippet += `${indent.repeat(2)}let paramSrc = param["src"] as! String\n`; + bodySnippet += `${indent.repeat(2)}let fileURL = URL(fileURLWithPath: paramSrc)\n`; + bodySnippet += `${indent.repeat(2)}if let fileContent = try? Data(contentsOf: fileURL) {\n`; + bodySnippet += `${indent.repeat(3)}body += Data("; filename=\\"\\(paramSrc)\\"\\r\\n".utf8)\n`; + bodySnippet += `${indent.repeat(3)}body += Data("Content-Type: \\"content-type header\\"\\r\\n".utf8)\n`; + bodySnippet += `${indent.repeat(3)}body += Data("\\r\\n".utf8)\n`; + bodySnippet += `${indent.repeat(3)}body += fileContent\n`; + bodySnippet += `${indent.repeat(3)}body += Data("\\r\\n".utf8)\n`; bodySnippet += `${indent.repeat(2)}}\n`; - bodySnippet += `${indent.repeat(2)}let paramType = param["type"] as! String\n`; - bodySnippet += `${indent.repeat(2)}if paramType == "text" {\n`; - bodySnippet += `${indent.repeat(3)}let paramValue = param["value"] as! String\n`; - bodySnippet += `${indent.repeat(3)}body += "\\r\\n\\r\\n\\(paramValue)\\r\\n"\n`; - bodySnippet += `${indent.repeat(2)}} else {\n`; - bodySnippet += `${indent.repeat(3)}let paramSrc = param["src"] as! String\n`; - bodySnippet += `${indent.repeat(3)}let fileData = try NSData(contentsOfFile:paramSrc, options:[]) as Data\n`; - bodySnippet += `${indent.repeat(3)}let fileContent = String(data: fileData, encoding: .utf8)!\n`; - bodySnippet += `${indent.repeat(3)}body += "; filename=\\"\\(paramSrc)\\"\\r\\n"\n`; - bodySnippet += `${indent.repeat(3)} + "Content-Type: \\"content-type header\\"\\r\\n\\r\\n`; - bodySnippet += '\\(fileContent)\\r\\n"\n'; - bodySnippet += `${indent.repeat(2)}}\n${indent}}\n}\nbody += "--\\(boundary)--\\r\\n";\n`; - bodySnippet += 'let postData = body.data(using: .utf8)'; + bodySnippet += `${indent}}\n`; + bodySnippet += '}\n'; + bodySnippet += 'body += Data("--\\(boundary)--\\r\\n".utf8);\n'; + bodySnippet += 'let postData = body\n'; return bodySnippet; } @@ -147,7 +152,7 @@ function parseFile () { // var bodySnippet = 'let filename = "{Insert_File_Name}", postData = Data()\n'; // bodySnippet += 'if let path = Bundle.main.path(forResource: filename, ofType: nil) {\n'; // bodySnippet += `${indent}do {\n${indent.repeat(2)}postData = - // try NSData(contentsOfFile:path, options:[]) as Data\n`; + // try NSData(contentsOfFile: path, options: []) as Data\n`; // bodySnippet += `${indent}} catch {\n`; // bodySnippet += `${indent.repeat(2)}print("Failed to read from \\(String(describing: filename))")\n`; // bodySnippet += `${indent}}\n} else {\n`; @@ -250,11 +255,11 @@ self = module.exports = { description: 'Remove white space and additional lines that may affect the server\'s response' }, { - name: 'Follow redirects', - id: 'followRedirect', + name: 'Include boilerplate', + id: 'includeBoilerplate', type: 'boolean', - default: true, - description: 'Automatically follow HTTP redirects' + default: false, + description: 'Include class definition and import statements in snippet' } ]; }, @@ -286,7 +291,8 @@ self = module.exports = { throw new Error('Swift-Converter: callback is not valid function'); } options = sanitizeOptions(options, self.getOptions()); - var codeSnippet, indent, trim, timeout, finalUrl, // followRedirect, + var indent, trim, timeout, finalUrl, + codeSnippet = '', bodySnippet = '', headerSnippet = '', requestBody; @@ -294,7 +300,6 @@ self = module.exports = { indent = options.indentType === 'Tab' ? '\t' : ' '; indent = indent.repeat(options.indentCount); timeout = options.requestTimeout; - // followRedirect = options.followRedirect; trim = options.trimRequestBody; finalUrl = getUrlStringfromUrlObject(request.url); @@ -342,9 +347,10 @@ self = module.exports = { requestBody = (request.body ? request.body.toJSON() : {}); bodySnippet = parseBody(requestBody, trim, indent); - codeSnippet = 'import Foundation\n'; - codeSnippet += '#if canImport(FoundationNetworking)\nimport FoundationNetworking\n#endif\n\n'; - codeSnippet += 'var semaphore = DispatchSemaphore (value: 0)\n\n'; + if (options.includeBoilerplate) { + codeSnippet += 'import Foundation\n'; + codeSnippet += '#if canImport(FoundationNetworking)\nimport FoundationNetworking\n#endif\n\n'; + } if (bodySnippet !== '') { codeSnippet += `${bodySnippet}\n\n`; } @@ -375,13 +381,14 @@ self = module.exports = { codeSnippet += '\nlet task = URLSession.shared.dataTask(with: request) { data, response, error in \n'; codeSnippet += `${indent}guard let data = data else {\n`; codeSnippet += `${indent.repeat(2)}print(String(describing: error))\n`; - codeSnippet += `${indent.repeat(2)}semaphore.signal()\n`; - codeSnippet += `${indent.repeat(2)}return\n`; + codeSnippet += `${indent.repeat(2)}`; + codeSnippet += options.includeBoilerplate ? 'exit(EXIT_SUCCESS)\n' : 'return\n'; codeSnippet += `${indent}}\n`; codeSnippet += `${indent}print(String(data: data, encoding: .utf8)!)\n`; - codeSnippet += `${indent}semaphore.signal()\n}\n\n`; + codeSnippet += options.includeBoilerplate ? `${indent}exit(EXIT_SUCCESS)\n` : ''; + codeSnippet += '}\n\n'; codeSnippet += 'task.resume()\n'; - codeSnippet += 'semaphore.wait()\n'; + codeSnippet += options.includeBoilerplate ? 'dispatchMain()\n' : ''; return callback(null, codeSnippet); } diff --git a/codegens/swift/lib/util.js b/codegens/swift/lib/util.js index 1f6d9fb38..0a83261ab 100644 --- a/codegens/swift/lib/util.js +++ b/codegens/swift/lib/util.js @@ -1,3 +1,5 @@ +const _ = require('./lodash'); + /** * Sanitizes input string by handling escape characters according to request body type * @@ -101,6 +103,51 @@ function sanitizeOptions (options, optionsArray) { return result; } +/** + * Encode param except the following characters- [,{,},],% + * + * @param {String} param + * @returns {String} + */ +function encodeParam (param) { + return encodeURIComponent(param) + .replace(/%5B/g, '[') + .replace(/%7B/g, '{') + .replace(/%5D/g, ']') + .replace(/%7D/g, '}') + .replace(/%2B/g, '+') + .replace(/%25/g, '%') + .replace(/'/g, '%27'); +} + +/** + * @param {Object} urlObject + * @returns {String} + */ +function getQueryString (urlObject) { + let isFirstParam = true, + params = _.get(urlObject, 'query.members'), + result = ''; + if (Array.isArray(params)) { + result = _.reduce(params, function (result, param) { + if (param.disabled === true) { + return result; + } + + if (isFirstParam) { + isFirstParam = false; + } + else { + result += '&'; + } + + return result + encodeParam(param.key) + '=' + encodeParam(param.value); + }, result); + } + + return result; +} + /** * * @param {*} urlObject The request sdk request.url object @@ -132,7 +179,7 @@ function getUrlStringfromUrlObject (urlObject) { url += urlObject.getPath(); } if (urlObject.query && urlObject.query.count()) { - let queryString = urlObject.getQueryString({ ignoreDisabled: true, encode: true }); + let queryString = getQueryString(urlObject); queryString && (url += '?' + queryString); } if (urlObject.hash) { diff --git a/codegens/swift/test/ci-install.sh b/codegens/swift/test/ci-install.sh new file mode 100755 index 000000000..e93e673fb --- /dev/null +++ b/codegens/swift/test/ci-install.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -ev; # stop on error + +echo "Installing dependencies required for tests in codegens/swift" +pushd ./codegens/swift &>/dev/null; + sudo apt-get update + sudo apt-get install clang-3.6 libicu-dev libpython2.7 -y + sudo apt-get install libcurl4 libpython2.7-dev -y + sudo wget -q https://download.swift.org/swift-5.7.3-release/ubuntu2004/swift-5.7.3-RELEASE/swift-5.7.3-RELEASE-ubuntu20.04.tar.gz + sudo tar xzf swift-5.7.3-RELEASE-ubuntu20.04.tar.gz + sudo chmod 777 swift-5.7.3-RELEASE-ubuntu20.04/usr/lib/swift/CoreFoundation/module.map +popd &>/dev/null; diff --git a/codegens/swift/test/newman/newman.test.js b/codegens/swift/test/newman/newman.test.js index a8ef9432b..c2384c9b5 100644 --- a/codegens/swift/test/newman/newman.test.js +++ b/codegens/swift/test/newman/newman.test.js @@ -1,16 +1,17 @@ var runNewmanTest = require('../../../../test/codegen/newman/newmanTestUtil').runNewmanTest, convert = require('../../lib/index').convert; -describe.skip('Convert for different types of request', function () { +describe('Convert for different types of request', function () { var options = { + includeBoilerplate: true, indentType: 'Space', indentCount: 4 }, // if running locally on mac change the runScript to 'swift snippet.swift' testConfig = { fileName: 'snippet.swift', - runScript: 'swift-5.3-RELEASE-ubuntu16.04/usr/bin/swift snippet.swift', - skipCollections: ['sameNameHeadersCollection'] + runScript: 'swift-5.7.3-RELEASE-ubuntu20.04/usr/bin/swift snippet.swift', + skipCollections: ['sameNameHeadersCollection', 'unsupportedMethods'] }; runNewmanTest(convert, options, testConfig); }); diff --git a/codegens/swift/test/unit/convert.test.js b/codegens/swift/test/unit/convert.test.js index cfec01c50..8b07ccc12 100644 --- a/codegens/swift/test/unit/convert.test.js +++ b/codegens/swift/test/unit/convert.test.js @@ -1,5 +1,6 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), + { Url } = require('postman-collection/lib/collection/url'), convert = require('../../index').convert, sanitize = require('../../lib/util').sanitize, getUrlStringfromUrlObject = require('../../lib/util').getUrlStringfromUrlObject, @@ -10,7 +11,7 @@ var expect = require('chai').expect, describe('Swift Converter', function () { describe('convert function', function () { - var request = new sdk.Request(mainCollection.item[0].request), + var request = new Request(mainCollection.item[0].request), snippetArray; const SINGLE_SPACE = ' '; // default indent type with indent count of 2 @@ -31,7 +32,7 @@ describe('Swift Converter', function () { }); it('should add content type if formdata field contains a content-type', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'body': { 'mode': 'formdata', @@ -62,7 +63,7 @@ describe('Swift Converter', function () { } expect(snippet).to.be.a('string'); expect(snippet).to.contain('if param["contentType"] != nil {'); - expect(snippet).to.contain('body += "\\r\\nContent-Type: \\(param["contentType"] as! String)"'); + expect(snippet).to.contain('body += Data("\\r\\nContent-Type: \\(param["contentType"] as! String)".utf8)'); }); }); @@ -95,7 +96,7 @@ describe('Swift Converter', function () { it('should not encode queryParam unresolved variables and ' + 'leave it inside double parenthesis {{xyz}}', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'url': { @@ -127,7 +128,7 @@ describe('Swift Converter', function () { }); it('should encode queryParams other than unresolved variables', function () { - request = new sdk.Request({ + request = new Request({ 'method': 'POST', 'header': [], 'url': { @@ -159,7 +160,7 @@ describe('Swift Converter', function () { }); it('should trim header keys and not trim header values', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'GET', 'header': [ { @@ -187,7 +188,7 @@ describe('Swift Converter', function () { }); it('should generate snippets for no files in form data', function () { - var request = new sdk.Request({ + var request = new Request({ 'method': 'POST', 'header': [], 'body': { @@ -238,7 +239,7 @@ describe('Swift Converter', function () { it('should generate valid snippets for single/double quotes in URL', function () { // url = https://a"b'c.com/'d/"e - var request = new sdk.Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes + var request = new Request("https://a\"b'c.com/'d/\"e"); // eslint-disable-line quotes convert(request, {}, function (error, snippet) { if (error) { expect.fail(null, null, error); @@ -254,7 +255,7 @@ describe('Swift Converter', function () { it('should return empty string for an url object for an empty url or if no url object is passed', function () { rawUrl = ''; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.be.empty; outputUrlString = getUrlStringfromUrlObject(); @@ -263,14 +264,14 @@ describe('Swift Converter', function () { it('should add protocol if present in the url object', function () { rawUrl = 'https://postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add the auth information if present in the url object', function () { rawUrl = 'https://user:password@postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); @@ -278,28 +279,28 @@ describe('Swift Converter', function () { it('should not add the auth information if user isn\'t present but' + ' password is present in the url object', function () { rawUrl = 'https://:password@postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include(':password'); }); it('should add host if present in the url object', function () { rawUrl = 'https://postman-echo.com'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add port if present in the url object', function () { rawUrl = 'https://postman-echo.com:8080'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); it('should add path if present in the url object', function () { rawUrl = 'https://postman-echo.com/get'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); @@ -308,7 +309,7 @@ describe('Swift Converter', function () { it('should not encode unresolved query params', function () { rawUrl = 'https://postman-echo.com/get?key={{value}}'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include('key=%7B%7Bvalue%7B%7B'); expect(outputUrlString).to.equal(rawUrl); @@ -316,7 +317,7 @@ describe('Swift Converter', function () { it('should encode query params other than unresolved variables', function () { rawUrl = 'https://postman-echo.com/get?key=\'a b c\''; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include('key=\'a b c\''); expect(outputUrlString).to.equal('https://postman-echo.com/get?key=%27a%20b%20c%27'); @@ -324,16 +325,23 @@ describe('Swift Converter', function () { it('should not encode unresolved query params and ' + 'encode every other query param, both present together', function () { - rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b c\''; - urlObject = new sdk.Url(rawUrl); + rawUrl = 'https://postman-echo.com/get?key1={{value}}&key2=\'a b+c\''; + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.not.include('key1=%7B%7Bvalue%7B%7B'); - expect(outputUrlString).to.not.include('key2=\'a b c\''); - expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b%20c%27'); + expect(outputUrlString).to.not.include('key2=\'a b+c\''); + expect(outputUrlString).to.equal('https://postman-echo.com/get?key1={{value}}&key2=%27a%20b+c%27'); + }); + + it('should not encode query params that are already encoded', function () { + rawUrl = 'https://postman-echo.com/get?query=urn%3Ali%3Afoo%3A62324'; + urlObject = new Url(rawUrl); + outputUrlString = getUrlStringfromUrlObject(urlObject); + expect(outputUrlString).to.equal('https://postman-echo.com/get?query=urn%3Ali%3Afoo%3A62324'); }); it('should discard disabled query params', function () { - urlObject = new sdk.Url({ + urlObject = new Url({ protocol: 'https', host: 'postman-echo.com', query: [ @@ -348,7 +356,7 @@ describe('Swift Converter', function () { it('should add hash if present in the url object', function () { rawUrl = 'https://postmanm-echo.com/get#hash'; - urlObject = new sdk.Url(rawUrl); + urlObject = new Url(rawUrl); outputUrlString = getUrlStringfromUrlObject(urlObject); expect(outputUrlString).to.equal(rawUrl); }); @@ -364,6 +372,7 @@ describe('Swift Converter', function () { expect(getOptions()[1]).to.have.property('id', 'indentType'); expect(getOptions()[2]).to.have.property('id', 'requestTimeout'); expect(getOptions()[3]).to.have.property('id', 'trimRequestBody'); + expect(getOptions()[4]).to.have.property('id', 'includeBoilerplate'); }); }); diff --git a/codegens/swift/test/unit/fixtures/testcollection/collection.json b/codegens/swift/test/unit/fixtures/testcollection/collection.json index ba436e23e..14489a9ae 100644 --- a/codegens/swift/test/unit/fixtures/testcollection/collection.json +++ b/codegens/swift/test/unit/fixtures/testcollection/collection.json @@ -50,11 +50,11 @@ "body": { }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -101,11 +101,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request?test=123&anotherone=232", + "raw": "https://postman-echo.com/request?test=123&anotherone=232", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -172,11 +172,11 @@ "raw": "\"'Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, \"id\" \"se\\\"mper\" sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat.'\"" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -337,11 +337,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request?hardik=\"me\"", + "raw": "https://postman-echo.com/request?hardik=\"me\"", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -398,11 +398,11 @@ "raw": "{\n \"json\": \"Test-Test\"\n}" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -582,11 +582,11 @@ "raw": "var val = 6;\nconsole.log(val);" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -637,11 +637,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -692,11 +692,11 @@ "raw": "\n Test Test\n" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -721,14 +721,13 @@ "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." }, "url": { - "raw": "https://mockbin.org/request/:action", + "raw": "https://postman-echo.com/:action", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ - "request", ":action" ], "variable": [ @@ -780,11 +779,11 @@ "raw": "Etiam mi lacus, cursus vitae felis et, blandit pellentesque neque. Vestibulum eget nisi a tortor commodo dignissim.\nQuisque ipsum ligula, faucibus a felis a, commodo elementum nisl. Mauris vulputate sapien et tincidunt viverra. Donec vitae velit nec metus." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -833,11 +832,11 @@ "raw": "Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat." }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -902,11 +901,11 @@ ] }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -957,11 +956,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -987,11 +986,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1015,11 +1014,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1043,11 +1042,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1071,11 +1070,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1094,11 +1093,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1108,7 +1107,7 @@ "response": [] }, { - "name": "PROFIND request", + "name": "PROPFIND request", "request": { "method": "PROPFIND", "header": [ @@ -1122,11 +1121,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1150,11 +1149,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1173,13 +1172,11 @@ "raw": "" }, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1194,13 +1191,11 @@ "raw": "" }, "url": { - "raw": "https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io", + "raw": "https://postman-echo.com", "protocol": "https", "host": [ - "9c76407d-5b8d-4b22-99fb-8c47a85d9848", - "mock", - "pstmn", - "io" + "postman-echo", + "com" ] } }, @@ -1288,7 +1283,7 @@ } ], "cookie": [], - "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://9c76407d-5b8d-4b22-99fb-8c47a85d9848.mock.pstmn.io\"\n}" + "body": "{\n \"args\": {},\n \"data\": \"Curabitur auctor, elit nec pulvinar porttitor, ex augue condimentum enim, eget suscipit urna felis quis neque.\\nSuspendisse sit amet luctus massa, nec venenatis mi. Suspendisse tincidunt massa at nibh efficitur fringilla. Nam quis congue mi. Etiam volutpat.\",\n \"files\": {},\n \"form\": {},\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"content-length\": \"256\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"content-type\": \"text/plain\",\n \"cookie\": \"sails.sid=s%3A1wOi4AdoZEbqBjGi6oSUC5Vlfje8wJvs.DHQfRLXfIBvZ%2Bv0KhLAThMDz%2FXvxh9gyxWYa0u1EZOU\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"x-forwarded-port\": \"443\",\n \"x-forwarded-proto\": \"https\"\n },\n \"json\": null,\n \"url\": \"https://postman-echo.com\"\n}" } ] }, @@ -1302,11 +1297,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1324,11 +1319,11 @@ "raw": "" }, "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/request", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ "request" @@ -1440,14 +1435,14 @@ { "expires": "Invalid Date", "httpOnly": true, - "domain": "mockbin.org", + "domain": "postman-echo.com", "path": "/", "secure": false, "value": "dfb94a3e1f3f8a9956138e4896847caf21521013330", "key": "__cfduid" } ], - "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://mockbin.org/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"mockbin.org\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" + "body": "{\n \"startedDateTime\": \"2018-03-14T09:06:37.443Z\",\n \"clientIPAddress\": \"106.51.70.154\",\n \"method\": \"COPY\",\n \"url\": \"https://postman-echo.com/request\",\n \"httpVersion\": \"HTTP/1.1\",\n \"cookies\": {\n \"__cfduid\": \"dfb94a3e1f3f8a9956138e4896847caf21521013330\"\n },\n \"headers\": {\n \"host\": \"postman-echo.com\",\n \"connection\": \"close\",\n \"accept-encoding\": \"gzip\",\n \"x-forwarded-for\": \"106.51.70.154, 172.68.255.127\",\n \"cf-ray\": \"3fb595d5facaa302-HKG\",\n \"x-forwarded-proto\": \"http\",\n \"cf-visitor\": \"{\\\"scheme\\\":\\\"https\\\"}\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"8d5b9832-75df-432f-90a3-284dacef0478\",\n \"user-agent\": \"PostmanRuntime/7.1.1\",\n \"accept\": \"*/*\",\n \"cookie\": \"__cfduid=dfb94a3e1f3f8a9956138e4896847caf21521013330\",\n \"cf-connecting-ip\": \"106.51.70.154\",\n \"x-request-id\": \"0e41473d-5130-4a6e-968d-b2a16cda3364\",\n \"x-forwarded-port\": \"80\",\n \"via\": \"1.1 vegur\",\n \"connect-time\": \"2\",\n \"x-request-start\": \"1521018397437\",\n \"total-route-time\": \"0\",\n \"content-length\": \"0\"\n },\n \"queryString\": {},\n \"postData\": {\n \"mimeType\": \"application/octet-stream\",\n \"text\": \"\",\n \"params\": []\n },\n \"headersSize\": 637,\n \"bodySize\": 0\n}" } ] } diff --git a/lib/assets/languageLabels.json b/lib/assets/languageLabels.json index 9709e331a..1cbed6bb9 100644 --- a/lib/assets/languageLabels.json +++ b/lib/assets/languageLabels.json @@ -6,6 +6,7 @@ "python": "Python", "ruby": "Ruby", "java": "Java", + "kotlin": "Kotlin", "c": "C", "php": "PHP", "objective-c": "Objective-C", @@ -18,5 +19,8 @@ "nodejs": "NodeJs", "ocaml": "OCaml", "shell": "Shell", - "dart": "Dart" + "dart": "Dart", + "r": "R", + "rust": "Rust", + "postman-cli": "Postman CLI" } diff --git a/lib/index.js b/lib/index.js index 6f319afa5..e99a505fc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,4 +1,4 @@ -var sdk = require('postman-collection'), +const { Request } = require('postman-collection/lib/collection/request'), labelList = require('./assets/languageLabels.json'), languageMap = require('./assets/languages.js'); @@ -11,14 +11,14 @@ module.exports = { * @param {Function} callback - callback function with arguments (error, object) */ getOptions (language, variant, callback) { - var validCodegen = languageMap.filter((codegen) => { - var lang = codegen.lang.trim(), + const validCodegen = languageMap.filter((codegen) => { + const lang = codegen.lang.trim(), currentVariant = codegen.variant.trim(); return language === lang.toLowerCase() && variant.toLowerCase() === currentVariant.toLowerCase(); }); validCodegen.forEach((codegen) => { - main = codegen.main; + const main = codegen.main; if (typeof main.getOptions !== 'function') { return callback('Codegen~getOptions: getOptions is not a function'); } @@ -35,10 +35,10 @@ module.exports = { * */ getLanguageList () { - var langMap = {}, + let langMap = {}, supportedLanguages = []; languageMap.forEach((codegen) => { - var lang = codegen.lang.trim(), + let lang = codegen.lang.trim(), syntax_mode = codegen.syntax_mode.trim(), variant = codegen.variant.trim(); lang = lang.toLowerCase(); @@ -84,14 +84,14 @@ module.exports = { * @param {Function} callback - callback function with arguments (error, string) */ convert (language, variant, request, options, callback) { - var convert, main; + let convert, main; - if (!sdk.Request.isRequest(request)) { + if (!Request.isRequest(request)) { return callback('Codegen~convert: Invalid request'); } languageMap.forEach((codegen) => { - var lang = codegen.lang.trim(), + const lang = codegen.lang.trim(), currentVariant = codegen.variant.trim(); if (language.toLowerCase() === lang.toLowerCase() && variant.toLowerCase() === currentVariant.toLowerCase()) { main = codegen.main; diff --git a/npm/boilerplate/.gitignore b/npm/boilerplate/.gitignore index 2c7c686e6..3040212d5 100644 --- a/npm/boilerplate/.gitignore +++ b/npm/boilerplate/.gitignore @@ -6,6 +6,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* +# Package manager lock files +yarn.lock +package-lock.json +pnpm-lock.yaml +bun.lockb + # Coverage directory used by tools like istanbul .coverage diff --git a/npm/ci-requirements.sh b/npm/ci-requirements.sh deleted file mode 100755 index e9821d070..000000000 --- a/npm/ci-requirements.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash -set -ev; # stop on error - -echo "Installing dependencies required for tests in codegens/java-okhttp" -pushd ./codegens/java-okhttp &>/dev/null; - sudo add-apt-repository ppa:openjdk-r/ppa -y - sudo rm -rf /var/lib/apt/lists/* - sudo apt-get update - sudo apt-get install -y openjdk-8-jdk - unzip test/unit/fixtures/dependencies.zip -popd &>/dev/null; - -echo "Installing dependencies required for tests in codegens/java-unirest" -pushd ./codegens/java-unirest &>/dev/null; - unzip test/unit/fixtures/dependencies.zip -popd &>/dev/null; - -echo "Installing dependencies required for tests in codegens/csharp-restsharp" -pushd ./codegens/csharp-restsharp &>/dev/null; - wget -q https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb - sudo dpkg -i packages-microsoft-prod.deb - sudo apt-get install apt-transport-https - sudo apt-get update - sudo apt-get install dotnet-sdk-2.2 - dotnet new console -o testProject - pushd ./testProject &>/dev/null; - dotnet add package RestSharp --version 106.15.0 - popd &>/dev/null; -popd &>/dev/null; - - -echo "Installing Powershell" - sudo apt-get install powershell -y - -echo "Installing dependencies required for tests in codegens/php-httprequest2" - pear install HTTP_Request2-2.3.0 - -echo "Installing dependencies required for tests in codegens/swift" -pushd ./codegens/swift &>/dev/null; - sudo apt-get update - sudo apt-get install clang-3.6 libicu-dev libpython2.7 -y - sudo apt-get install libcurl3 libpython2.7-dev -y - sudo wget https://swift.org/builds/swift-5.3-release/ubuntu1604/swift-5.3-RELEASE/swift-5.3-RELEASE-ubuntu16.04.tar.gz - sudo tar xzf swift-5.3-RELEASE-ubuntu16.04.tar.gz - sudo chmod 777 swift-5.3-RELEASE-ubuntu16.04/usr/lib/swift/CoreFoundation/module.map -popd &>/dev/null; - -echo "Installing dependencies required for tests in codegens/csharp-restsharp" -sudo apt-get install -y mono-complete - -echo "Installing curl v7.68" - sudo apt-get install -y libssl-dev autoconf libtool make - wget https://curl.haxx.se/download/curl-7.68.0.zip - unzip curl-7.68.0.zip - pushd ./curl-7.68.0 &>/dev/null - ./buildconf - ./configure --with-ssl - make - sudo make install - sudo cp /usr/local/bin/curl /usr/bin/curl - sudo ldconfig - popd &>/dev/null - -echo "Installing dependencies required for tests in codegens/shell-httpie" -sudo apt-get install httpie - -echo "Installing dependencies required for tests in codegens/dart" -pushd ./codegens/dart-http &>/dev/null; - wget https://storage.googleapis.com/dart-archive/channels/stable/release/2.10.2/linux_packages/dart_2.10.2-1_amd64.deb - sudo dpkg -i dart_2.10.2-1_amd64.deb - echo '''name: test -dependencies: - http: ^0.12.2 -''' > pubspec.yaml - dart pub get -popd &>/dev/null; - -echo "Installing dependencies required for tests in codegens/php-guzzle" - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" - php composer-setup.php - php -r "unlink('composer-setup.php');" - sudo mv composer.phar /usr/bin/composer - composer global require guzzlehttp/guzzle:7.4.1 diff --git a/npm/cirequirements.js b/npm/cirequirements.js new file mode 100644 index 000000000..44f918ce4 --- /dev/null +++ b/npm/cirequirements.js @@ -0,0 +1,38 @@ +const path = require('path'), + fs = require('fs'), + shell = require('shelljs'); + +const getSubfolders = (folder) => { + return fs.readdirSync(folder) + .map((subfolder) => { return { path: path.join(folder, subfolder), name: subfolder}; }) + .filter((obj) => { return fs.statSync(obj.path).isDirectory(); }); + }, + + args = process.argv, + PATH_TO_CODEGENS_FOLDER = path.resolve(__dirname, '../codegens'); + +getSubfolders(PATH_TO_CODEGENS_FOLDER) + .filter((codegen) => { + // check if specific codegen is requested + const requestedCodegen = args.length > 2 ? args[2] : null; + if (requestedCodegen === null) { + return true; + } + return codegen.name === requestedCodegen; + }) + .filter((codegen) => { + // check if requested codegen has a installation script + return fs.existsSync(path.join(codegen.path, 'test', 'ci-install.sh')); + }).forEach((codegen) => { + console.log('Installing CI dependencies for codegen: ' + codegen.name); + const commandOut = shell.exec(path.join(codegen.path, 'test', 'ci-install.sh')); + + if (commandOut.code !== 0) { + console.error('Failed to install CI dependencies for codegen: ' + codegen.name); + console.error(commandOut.stderr); + process.exit(1); + } + + console.log(commandOut.stdout); + }); + diff --git a/npm/deepinstall.js b/npm/deepinstall.js index e3741a9af..802d83863 100644 --- a/npm/deepinstall.js +++ b/npm/deepinstall.js @@ -1,20 +1,16 @@ var shell = require('shelljs'), path = require('path'), async = require('async'), - PRODUCTION_FLAG = '', + { detect, getNpmVersion } = require('detect-package-manager'), + pm, + ver, + command, getSubfolders, fs = require('fs'), pwd = shell.pwd(); const args = process.argv, PATH_TO_CODEGENS_FOLDER = path.resolve(__dirname, '../codegens'); -if (args[2] && args[2] === 'dev') { - console.log('Dev flag detected running npm install'); -} -else { - PRODUCTION_FLAG = '--no-audit --production'; -} - getSubfolders = (folder) => { return fs.readdirSync(folder) .map((subfolder) => { return { path: path.join(folder, subfolder), name: subfolder}; }) @@ -23,6 +19,42 @@ getSubfolders = (folder) => { async.series([ function (next) { + detect().then((res) => { + pm = res; + console.log('Detected package manager: ' + pm); + return next(); + }); + }, + function (next) { + getNpmVersion(pm).then((res) => { + ver = res; + console.log('Detected ' + pm + ' version: ' + ver); + return next(); + }); + }, + function (next) { + if (args[2] && args[2] === 'dev') { + console.log('Dev flag detected running ' + pm + ' install'); + command = pm + ' install'; + } + else { + switch (pm) { + case 'yarn': + if (ver.startsWith('1')) { + command = 'yarn install --production --frozen-lockfile'; + } + else { + command = 'touch yarn.lock && yarn workspaces focus --all --production' + } + break; + case 'pnpm': + command = 'pnpm install --ignore-workspace --prod'; + break; + default: + command = pm + ' install --no-audit --production'; + } + } + console.log('Running pre-package script'); var prepackagePath = path.resolve(__dirname, 'pre-package.js'), commandOutput = shell.exec(`node "${prepackagePath}"`); @@ -42,11 +74,11 @@ async.series([ var commandOut; - console.log(codegen.name + ': npm install ' + PRODUCTION_FLAG); - commandOut = shell.exec('npm install ' + PRODUCTION_FLAG, { silent: true }); + console.log(codegen.name + ': ' + command); + commandOut = shell.exec(command, { silent: true }); if (commandOut.code !== 0) { - console.error('Failed to run npm install on codegen ' + codegen.name + ', here is the error:'); + console.error('Failed to run ' + pm + ' install on codegen ' + codegen.name + ', here is the error:'); return next(commandOut.stderr); } console.log(commandOut.stdout); diff --git a/npm/test.sh b/npm/test.sh index a2757b4a0..6b340c5d0 100755 --- a/npm/test.sh +++ b/npm/test.sh @@ -59,6 +59,9 @@ else # check for .gitignore, license.md, readme.md, .eslintrc and package.json mocha ./test/system/repository.test.js; + # runs test to see if the codegen interface is implemented correctly + mocha ./test/unit/lib.test.js; + # Common structure and npm test for each codegen. echo -e "Running codegen-structure tests on all the codegens"; for directory in codegens/*; do diff --git a/package-lock.json b/package-lock.json index 2010210c3..780c7a78f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "postman-code-generators", - "version": "1.1.5", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -142,6 +142,11 @@ "to-fast-properties": "^2.0.0" } }, + "@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==" + }, "@postman/form-data": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", @@ -184,16 +189,6 @@ "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", "dev": true }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, "abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -210,13 +205,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz", "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==", - "dev": true + "dev": true, + "requires": {} }, "acorn-jsx": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true + "dev": true, + "requires": {} }, "acorn-node": { "version": "1.6.2", @@ -277,6 +274,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -351,11 +349,6 @@ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", "dev": true }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" - }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -433,12 +426,9 @@ "dev": true }, "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "requires": { - "lodash": "^4.17.14" - } + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.2.tgz", + "integrity": "sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==" }, "async-each": { "version": "1.0.3", @@ -622,9 +612,9 @@ "integrity": "sha512-erYug8XoqzU3IfcU8fUgyHqyOXqIE4tUTTQ+7mqUjQlvnXkOO6OlT9c/ZoJVHYoAaqGxr09CN53G7XIsO4KtWA==", "dev": true, "requires": { - "JSONStream": "^1.0.3", "combine-source-map": "~0.8.0", "defined": "^1.0.0", + "JSONStream": "^1.0.3", "safe-buffer": "^5.1.1", "through2": "^2.0.0", "umd": "^3.0.0" @@ -659,7 +649,6 @@ "integrity": "sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g==", "dev": true, "requires": { - "JSONStream": "^1.0.3", "assert": "^1.4.0", "browser-pack": "^6.0.1", "browser-resolve": "^1.11.0", @@ -681,6 +670,7 @@ "https-browserify": "^1.0.0", "inherits": "~2.0.1", "insert-module-globals": "^7.0.0", + "JSONStream": "^1.0.3", "labeled-stream-splicer": "^2.0.0", "module-deps": "^4.0.8", "os-browserify": "~0.3.0", @@ -897,14 +887,14 @@ } }, "chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", - "deep-eql": "^3.0.1", + "deep-eql": "^4.1.2", "get-func-name": "^2.0.0", "loupe": "^2.3.1", "pathval": "^1.1.1", @@ -1155,6 +1145,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -1162,7 +1153,8 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true }, "colors": { "version": "1.4.0", @@ -1423,9 +1415,9 @@ "dev": true }, "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", "dev": true, "requires": { "type-detect": "^4.0.0" @@ -1509,19 +1501,46 @@ "dev": true }, "dependency-check": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/dependency-check/-/dependency-check-2.10.1.tgz", - "integrity": "sha512-gmLQXELyRvWwy0IeSOMgqRvs5lotLhMO9n5932lfXhkyZ7i7wqAQ/zBoued07qRvgvo9Byol98sG8HbYKoTpNA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dependency-check/-/dependency-check-3.0.0.tgz", + "integrity": "sha1-kuKkFBgS1OmcuRKYw2p0NbhIscY=", "dev": true, "requires": { - "async": "^2.1.4", "builtins": "^2.0.0", - "debug": "^2.2.0", - "detective": "^4.0.0", - "is-relative": "^0.2.1", + "debug": "^3.1.0", + "detective": "^5.0.2", + "is-relative": "^1.0.0", "minimist": "^1.2.0", - "read-package-json": "^1.3.3", + "read-package-json": "^2.0.10", "resolve": "^1.1.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "detective": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-5.2.0.tgz", + "integrity": "sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==", + "dev": true, + "requires": { + "acorn-node": "^1.6.1", + "defined": "^1.0.0", + "minimist": "^1.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } } }, "deps-sort": { @@ -1546,6 +1565,104 @@ "minimalistic-assert": "^1.0.0" } }, + "detect-package-manager": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-3.0.2.tgz", + "integrity": "sha512-8JFjJHutStYrfWwzfretQoyNGoZVW1Fsrp4JO9spa7h/fBfwgTMEIy4/LBzRDGsxwVPHU0q+T9YvwLDJoOApLQ==", + "requires": { + "execa": "^5.1.1" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "detective": { "version": "4.7.1", "resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz", @@ -1590,43 +1707,12 @@ "esutils": "^2.0.2" } }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, "domain-browser": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", "dev": true }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -1688,11 +1774,6 @@ "once": "^1.4.0" } }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1761,15 +1842,11 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true }, "escodegen": { "version": "1.8.1", @@ -1899,12 +1976,23 @@ } }, "eslint-plugin-security": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.4.0.tgz", - "integrity": "sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-1.5.0.tgz", + "integrity": "sha512-hAFVwLZ/UeXrlyVD2TDarv/x00CoFVpaY0IUZhKjPjiFxqkuQVixsK4f2rxngeQOqSxi6OUjzJM/jMwKEVjJ8g==", "dev": true, "requires": { - "safe-regex": "^1.1.0" + "safe-regex": "^2.1.1" + }, + "dependencies": { + "safe-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", + "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", + "dev": true, + "requires": { + "regexp-tree": "~0.1.1" + } + } } }, "eslint-scope": { @@ -2848,24 +2936,24 @@ "dev": true, "optional": true }, - "string-width": { - "version": "1.0.2", + "string_decoder": { + "version": "1.1.1", "bundled": true, "dev": true, "optional": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "safe-buffer": "~5.1.0" } }, - "string_decoder": { - "version": "1.1.1", + "string-width": { + "version": "1.0.2", "bundled": true, "dev": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "strip-ansi": { @@ -3002,18 +3090,6 @@ "assert-plus": "^1.0.0" } }, - "github-url-from-git": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-url-from-git/-/github-url-from-git-1.5.0.tgz", - "integrity": "sha1-+YX+3MCpqledyI16/waNVcxiUaA=", - "dev": true - }, - "github-url-from-username-repo": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/github-url-from-username-repo/-/github-url-from-username-repo-1.0.2.tgz", - "integrity": "sha1-fdeTMNKr5pwQws73lxTJchV5Hfo=", - "dev": true - }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -3111,15 +3187,6 @@ "function-bind": "^1.1.1" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, "has-bigints": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", @@ -3237,39 +3304,6 @@ "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", "dev": true }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", - "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "string_decoder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", - "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "http-reasons": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/http-reasons/-/http-reasons-0.1.0.tgz", @@ -3308,6 +3342,11 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3418,11 +3457,11 @@ "integrity": "sha512-VE6NlW+WGn2/AeOMd496AHFYmE7eLKkUY6Ty31k4og5vmA3Fjuwe9v6ifH6Xx/Hz27QvdoMoviw1/pqWRB09Sw==", "dev": true, "requires": { - "JSONStream": "^1.0.3", "acorn-node": "^1.5.2", "combine-source-map": "^0.8.0", "concat-stream": "^1.6.1", "is-buffer": "^1.1.0", + "JSONStream": "^1.0.3", "path-is-absolute": "^1.0.1", "process": "~0.11.0", "through2": "^2.0.0", @@ -3444,56 +3483,6 @@ } } }, - "intel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/intel/-/intel-1.2.0.tgz", - "integrity": "sha1-EdEUfraz9Fgr31M3s31UFYTp5B4=", - "dev": true, - "requires": { - "chalk": "^1.1.0", - "dbug": "~0.4.2", - "stack-trace": "~0.0.9", - "strftime": "~0.10.0", - "symbol": "~0.3.1", - "utcstring": "~0.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -3718,12 +3707,12 @@ } }, "is-relative": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz", - "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "is-unc-path": "^0.1.1" + "is-unc-path": "^1.0.0" } }, "is-shared-array-buffer": { @@ -3763,12 +3752,12 @@ "dev": true }, "is-unc-path": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz", - "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { - "unc-path-regex": "^0.1.0" + "unc-path-regex": "^0.1.2" } }, "is-weakref": { @@ -3795,8 +3784,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -3991,12 +3979,6 @@ "handlebars": "^4.1.2" } }, - "jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", - "dev": true - }, "js-sha512": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz", @@ -4004,9 +3986,9 @@ "dev": true }, "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -4107,14 +4089,11 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, - "json-parse-helpfulerror": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", - "integrity": "sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w=", - "dev": true, - "requires": { - "jju": "^1.1.0" - } + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "json-schema": { "version": "0.4.0", @@ -4161,6 +4140,16 @@ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", "dev": true }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -4272,43 +4261,18 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" - }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", "dev": true }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=", "dev": true }, - "lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" - }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -4316,12 +4280,12 @@ "dev": true, "requires": { "chalk": "^2.0.1" - } + } }, "loupe": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", - "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", "dev": true, "requires": { "get-func-name": "^2.0.0" @@ -4402,7 +4366,8 @@ "version": "8.4.1", "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.4.1.tgz", "integrity": "sha512-sLODeRetZ/61KkKLJElaU3NuU2z7MhXf12Ml1WJMSdwpngeofneCRF+JBbat8HiSqhniOMuTemXMrsI7hA6XyA==", - "dev": true + "dev": true, + "requires": {} }, "md5.js": { "version": "1.3.5", @@ -4457,6 +4422,11 @@ } } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -4495,10 +4465,9 @@ "dev": true }, "mime-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.0.tgz", - "integrity": "sha1-4p+IkeKE14JwJG8AUNaDS9u+EzI=", - "dev": true, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.2.tgz", + "integrity": "sha512-Y5ERWVcyh3sby9Fx2U5F1yatiTFjNsqF5NltihTWI9QgNtr5o3dbCZdcKa1l2wyfhnwwoP9HGNxga7LqZLA6gw==", "requires": { "charset": "^1.0.0" } @@ -4714,7 +4683,6 @@ "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", "dev": true, "requires": { - "JSONStream": "^1.0.3", "browser-resolve": "^1.7.0", "cached-path-relative": "^1.0.0", "concat-stream": "~1.5.0", @@ -4722,6 +4690,7 @@ "detective": "^4.0.0", "duplexer2": "^0.1.2", "inherits": "^2.0.1", + "JSONStream": "^1.0.3", "parents": "^1.0.0", "readable-stream": "^2.0.2", "resolve": "^1.1.3", @@ -4769,13 +4738,6 @@ "to-regex": "^3.0.1" } }, - "natives": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", - "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==", - "dev": true, - "optional": true - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5004,6 +4966,12 @@ "remove-trailing-separator": "^1.0.1" } }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -5013,11 +4981,6 @@ "path-key": "^2.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "nyc": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", @@ -5495,141 +5458,54 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, - "postcss": { - "version": "7.0.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz", - "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==", - "requires": { - "chalk": "^2.4.2", - "source-map": "^0.6.1", - "supports-color": "^6.1.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "postman-collection": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-3.6.11.tgz", - "integrity": "sha512-22oIsOXwigdEGQJuTgS44964hj0/gN20E6/aiDoO469WiqqOk5JEEVQpW8zCDjsb9vynyk384JqE9zRyvfrH5A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postman-collection/-/postman-collection-5.0.0.tgz", + "integrity": "sha512-1LK795Atv/ZX3jK1MCTx9KCBz0rAiIJJhTLqnJ4AsXLiLSqJuAH1w5jI1CQzHVLpPFg6E8Rl4tQIhF0eBgKNQQ==", "requires": { - "escape-html": "1.0.3", - "faker": "5.5.3", + "@faker-js/faker": "5.5.3", "file-type": "3.9.0", "http-reasons": "0.1.0", - "iconv-lite": "0.6.2", + "iconv-lite": "0.6.3", "liquid-json": "0.3.1", "lodash": "4.17.21", - "marked": "2.0.1", - "mime-format": "2.0.1", - "mime-types": "2.1.30", - "postman-url-encoder": "3.0.1", - "sanitize-html": "1.20.1", - "semver": "7.3.5", - "uuid": "3.4.0" + "mime-format": "2.0.2", + "mime-types": "2.1.35", + "postman-url-encoder": "3.0.6", + "semver": "7.7.1", + "uuid": "8.3.2" }, "dependencies": { "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "marked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.1.tgz", - "integrity": "sha512-5+/fKgMv2hARmMW7DOpykr2iLhl0NgjyELk5yn92iE7z8Se1IS9n3UsFm86hFXIkvMBmVxki8+ckcpjBeyo/hw==" - }, "mime-db": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", - "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" - }, - "mime-format": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mime-format/-/mime-format-2.0.1.tgz", - "integrity": "sha512-XxU3ngPbEnrYnNbIX+lYSaYg0M01v6p2ntd2YaFksTu0vayaw5OJvbdRyWs07EYRlLED5qadUZ+xo+XhOvFhwg==", - "requires": { - "charset": "^1.0.0" - } + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { - "version": "2.1.30", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", - "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "requires": { - "mime-db": "1.47.0" + "mime-db": "1.52.0" } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" }, "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, @@ -5909,17 +5785,17 @@ } }, "postman-url-encoder": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.1.tgz", - "integrity": "sha512-dMPqXnkDlstM2Eya+Gw4MIGWEan8TzldDcUKZIhZUsJ/G5JjubfQPhFhVWKzuATDMvwvrWbSjF+8VmAvbu6giw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/postman-url-encoder/-/postman-url-encoder-3.0.6.tgz", + "integrity": "sha512-uOlnZW+4Cmpbfbuq02hdj1hSpcIFmQxlAwsO6dflwUIVpt9+1duYVxXv3ikf+wHrAO8Wy98uVKnnuR8R0Qpdng==", "requires": { - "punycode": "^2.1.1" + "punycode": "^2.3.1" }, "dependencies": { "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" } } }, @@ -6050,57 +5926,15 @@ } }, "read-package-json": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-1.3.3.tgz", - "integrity": "sha1-73nf2kbhZTdu6KV++/7dTRsCm6Q=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", "dev": true, "requires": { - "glob": "^5.0.3", - "graceful-fs": "2 || 3", - "json-parse-helpfulerror": "^1.0.2", - "normalize-package-data": "^1.0.0" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.12.tgz", - "integrity": "sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==", - "dev": true, - "optional": true, - "requires": { - "natives": "^1.1.3" - } - }, - "normalize-package-data": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-1.0.3.tgz", - "integrity": "sha1-i+lVuJB6+XXxpFhOqLubQUkjEvU=", - "dev": true, - "requires": { - "github-url-from-git": "^1.3.0", - "github-url-from-username-repo": "^1.0.0", - "semver": "2 || 3 || 4" - } - }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - } + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" } }, "read-pkg": { @@ -6170,12 +6004,23 @@ } }, "recursive-readdir": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz", - "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", "dev": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.5" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } }, "regex-not": { @@ -6188,6 +6033,12 @@ "safe-regex": "^1.1.0" } }, + "regexp-tree": { + "version": "0.1.24", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz", + "integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==", + "dev": true + }, "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", @@ -6315,7 +6166,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -6331,48 +6183,6 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sanitize-html": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-1.20.1.tgz", - "integrity": "sha512-txnH8TQjaQvg2Q0HY06G6CDJLVYCpbnxrdO0WN8gjCKaU5J0KbyGYhZxx5QJg3WLZ1lB7XU9kDkfrCXUozqptA==", - "requires": { - "chalk": "^2.4.1", - "htmlparser2": "^3.10.0", - "lodash.clonedeep": "^4.5.0", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.mergewith": "^4.6.1", - "postcss": "^7.0.5", - "srcset": "^1.0.0", - "xtend": "^4.0.1" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", @@ -6467,9 +6277,9 @@ } }, "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "requires": { "glob": "^7.0.0", "interpret": "^1.0.0", @@ -6709,15 +6519,6 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "srcset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/srcset/-/srcset-1.0.0.tgz", - "integrity": "sha1-pWad4StC87HV6D7QPHEEb8SPQe8=", - "requires": { - "array-uniq": "^1.0.2", - "number-is-nan": "^1.0.0" - } - }, "sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -6802,6 +6603,14 @@ "dev": true, "requires": { "bluebird": "^2.6.2" + }, + "dependencies": { + "bluebird": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", + "dev": true + } } }, "stream-splicer": { @@ -6814,6 +6623,15 @@ "readable-stream": "^2.0.2" } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -6844,15 +6662,6 @@ "define-properties": "^1.1.3" } }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -6882,6 +6691,11 @@ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -7330,7 +7144,8 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true }, "uuid": { "version": "3.3.2", @@ -7406,7 +7221,6 @@ "integrity": "sha512-zQt/Gd1+W+IY+h/xX2NYMW4orQWhqSwyV+xsblycTtpOuB27h1fZhhNQuipJ4t79ohw4P4mMem0jp/ZkISQtjQ==", "dev": true, "requires": { - "JSONStream": "^1.0.3", "assert": "^1.4.0", "browser-pack": "^6.0.1", "browser-resolve": "^1.11.0", @@ -7428,6 +7242,7 @@ "https-browserify": "^1.0.0", "inherits": "~2.0.1", "insert-module-globals": "^7.0.0", + "JSONStream": "^1.0.3", "labeled-stream-splicer": "^2.0.0", "mkdirp": "^0.5.0", "module-deps": "^6.0.0", @@ -7506,7 +7321,6 @@ "integrity": "sha512-hKPmO06so6bL/ZvqVNVqdTVO8UAYsi3tQWlCa+z9KuWhoN4KDQtb5hcqQQv58qYiDE21wIvnttZEPiDgEbpwbA==", "dev": true, "requires": { - "JSONStream": "^1.0.3", "browser-resolve": "^1.7.0", "cached-path-relative": "^1.0.0", "concat-stream": "~1.6.0", @@ -7514,6 +7328,7 @@ "detective": "^5.0.2", "duplexer2": "^0.1.2", "inherits": "^2.0.1", + "JSONStream": "^1.0.3", "parents": "^1.0.0", "readable-stream": "^2.0.2", "resolve": "^1.4.0", @@ -7680,7 +7495,8 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true }, "y18n": { "version": "4.0.0", diff --git a/package.json b/package.json index 51fe30a01..5831bb94b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postman-code-generators", - "version": "1.1.5", + "version": "2.1.0", "description": "Generates code snippets for a postman collection", "main": "index.js", "directories": { @@ -14,7 +14,7 @@ "postinstall": "node npm/deepinstall.js", "deepinstall": "node npm/deepinstall.js", "boilerplate": "node npm/boilerplate.js", - "cirequirements": "npm/ci-requirements.sh", + "cirequirements": "node npm/cirequirements.js", "test-lint": "node npm/test-lint.js" }, "repository": { @@ -25,35 +25,36 @@ "license": "Apache-2.0", "homepage": "https://github.com/postmanlabs/code-generators", "dependencies": { - "async": "2.6.3", + "async": "3.2.2", + "detect-package-manager": "3.0.2", + "lodash": "4.17.21", "path": "0.12.7", - "postman-collection": "3.6.11", - "shelljs": "0.8.4" + "postman-collection": "^5.0.0", + "shelljs": "0.8.5" }, "devDependencies": { "browserify": "14.5.0", - "chai": "4.3.6", + "chai": "4.3.7", "chalk": "2.4.2", - "eslint": "5.16.0", - "dependency-check": "2.10.1", + "dependency-check": "3.0.0", "editorconfig": "0.15.3", + "eslint": "5.16.0", "eslint-plugin-jsdoc": "3.15.1", "eslint-plugin-lodash": "2.7.0", "eslint-plugin-mocha": "4.12.1", - "eslint-plugin-security": "1.4.0", + "eslint-plugin-security": "1.5.0", "istanbul": "0.4.5", - "js-yaml": "3.14.0", - "newman": "5.3.2", + "js-yaml": "3.14.1", "jsdoc": "3.6.10", "mocha": "6.2.3", - "lodash": "4.17.21", + "newman": "5.3.2", "nyc": "14.1.1", "parse-gitignore": "1.0.1", "pretty-ms": "3.2.0", - "recursive-readdir": "2.2.2", + "recursive-readdir": "2.2.3", "watchify": "3.11.1" }, "engines": { - "node": ">=6" + "node": ">=18" } } diff --git a/test/codegen/newman/fixtures/basicCollection.json b/test/codegen/newman/fixtures/basicCollection.json index 0c0f9c73e..43c61e004 100644 --- a/test/codegen/newman/fixtures/basicCollection.json +++ b/test/codegen/newman/fixtures/basicCollection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "f52ee07d-6345-4220-89af-e6696b3c0122", + "_postman_id": "b303fb8b-9b21-4429-b00f-33b6a7efa186", "name": "Basic Collection", "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" @@ -12,7 +12,6 @@ { "listen": "test", "script": { - "id": "34edbfa7-7d32-42d6-8397-af2378c3aaa4", "exec": [ "" ], @@ -53,7 +52,6 @@ }, { "name": "Request Headers", - "event": [], "request": { "method": "GET", "header": [ @@ -95,7 +93,6 @@ { "listen": "test", "script": { - "id": "e150d55b-0273-430a-9e1d-11969b433734", "exec": [ "" ], @@ -141,7 +138,6 @@ { "listen": "test", "script": { - "id": "1bfe1fc3-c244-4a42-83c5-1a0d94d56ffd", "exec": [ "" ], @@ -188,7 +184,6 @@ { "listen": "test", "script": { - "id": "a3ddecd1-e89d-426d-995c-0d6a678caa91", "exec": [ "var responseJSON;", "", @@ -216,8 +211,7 @@ "header": [ { "key": "Content-Type", - "value": "text/plain", - "disabled": false + "value": "text/plain" } ], "body": { @@ -245,7 +239,6 @@ { "listen": "test", "script": { - "id": "e926912d-1c99-4c54-9b53-91c8f63acef0", "exec": [ "" ], @@ -312,7 +305,6 @@ { "listen": "test", "script": { - "id": "d211bdad-60b3-4cd6-869f-853377bf03ef", "exec": [ "" ], @@ -353,7 +345,6 @@ { "listen": "test", "script": { - "id": "532fef57-48fd-4ffe-ac7e-f5a7e32facc2", "exec": [ "" ], @@ -394,7 +385,6 @@ { "listen": "test", "script": { - "id": "8bbbbc5b-2983-4979-8347-3ced95a69f7e", "exec": [ "" ], @@ -435,7 +425,6 @@ { "listen": "test", "script": { - "id": "48da0505-470f-4cf3-bb77-30665415af60", "exec": [ "" ], @@ -618,7 +607,6 @@ { "listen": "test", "script": { - "type": "text/javascript", "exec": [ "var responseJSON;", "", @@ -634,28 +622,23 @@ "", "tests['response has PUT data'] = _.has(responseJSON, 'data');", "tests['response matches the data sent in request'] = (responseJSON.data && responseJSON.data.length === 256);" - ] + ], + "type": "text/javascript" } } ], "request": { "method": "DELETE", - "header": [ - { - "key": "Content-Type", - "value": "text/plain" - } - ], - "body": {}, + "header": [], "url": { - "raw": "https://mockbin.org/request", + "raw": "https://postman-echo.com/delete", "protocol": "https", "host": [ - "mockbin", - "org" + "postman-echo", + "com" ], "path": [ - "request" + "delete" ] }, "description": "The HTTP `DELETE` method is used to delete resources on a server. The exact\nuse of `DELETE` requests depends on the server implementation. In general, \n`DELETE` requests support both, Query String parameters as well as a Request \nBody.\n\nThis endpoint accepts an HTTP `DELETE` request and provides debug information\nsuch as the HTTP headers, Query String arguments, and the Request Body." @@ -667,7 +650,6 @@ { "listen": "prerequest", "script": { - "id": "e80b6162-6c90-4150-bfa1-7f42f11c8f64", "type": "text/javascript", "exec": [ "" @@ -677,7 +659,6 @@ { "listen": "test", "script": { - "id": "538efa04-97ce-456c-a5a1-772c466591d5", "type": "text/javascript", "exec": [ "" diff --git a/test/codegen/newman/fixtures/formdataFileCollection.json b/test/codegen/newman/fixtures/formdataFileCollection.json index 95ba28c1e..3f15e2f9e 100644 --- a/test/codegen/newman/fixtures/formdataFileCollection.json +++ b/test/codegen/newman/fixtures/formdataFileCollection.json @@ -17,15 +17,15 @@ "key": "single file", "value": "", "type": "file", - "src": "/Users/luis.tejeda/Documents/Source/GitHub/postmanlabs/postman-code-generators/dummyFile1.txt" + "src": "/Users/vishalkumar.shingala@postman.com/pm/postman-code-generators/dummyFile1.txt" }, { "key": "multiple files", "value": "", "type": "file", "src": [ - "/Users/luis.tejeda/Documents/Source/GitHub/postmanlabs/postman-code-generators/dummyFile2.txt", - "/Users/luis.tejeda/Documents/Source/GitHub/postmanlabs/postman-code-generators/dummyFile3.txt" + "/Users/vishalkumar.shingala@postman.com/pm/postman-code-generators/dummyFile2.txt", + "/Users/vishalkumar.shingala@postman.com/pm/postman-code-generators/dummyFile3.txt" ] } ] @@ -55,7 +55,7 @@ "key": "binary file", "value": "", "type": "file", - "src": "/Users/luis.tejeda/Documents/Source/GitHub/postmanlabs/postman-code-generators/dummyBinaryFile" + "src": "/Users/vishalkumar.shingala@postman.com/pm/postman-code-generators/dummyBinaryFile" } ] }, diff --git a/test/codegen/newman/fixtures/queryParamsCollection.json b/test/codegen/newman/fixtures/queryParamsCollection.json new file mode 100644 index 000000000..6420f6a29 --- /dev/null +++ b/test/codegen/newman/fixtures/queryParamsCollection.json @@ -0,0 +1,31 @@ +{ + "info": { + "_postman_id": "0bb73926-93f7-4ad4-a191-6a5d9e3460ef", + "name": "Query Params Collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Query Param with encoded value", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://postman-echo.com/query=urn%3Ali%3Afoo%3A62324", + "host": [ + "https://postman-echo.com" + ], + "path": [ + "get" + ], + "query": [ + { + "key": "query", + "value": "urn%3Ali%3Afoo%3A62324" + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/test/codegen/newman/fixtures/rawBody.json b/test/codegen/newman/fixtures/rawBody.json new file mode 100644 index 000000000..754451109 --- /dev/null +++ b/test/codegen/newman/fixtures/rawBody.json @@ -0,0 +1,39 @@ +{ + "info": { + "_postman_id": "f52ee07d-6345-4220-89af-e6696b3c0122", + "name": "Basic Collection", + "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "POST raw with quoted text", + "event": [], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"Hello\": \"{\\\"hello_world\\\": true}\"\n}" + }, + "url": { + "raw": "https://postman-echo.com/post", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "post" + ] + } + }, + "response": [] + } + ] +} \ No newline at end of file diff --git a/test/codegen/newman/fixtures/redirectCollection.json b/test/codegen/newman/fixtures/redirectCollection.json index 5fb851c47..15a4c15ba 100644 --- a/test/codegen/newman/fixtures/redirectCollection.json +++ b/test/codegen/newman/fixtures/redirectCollection.json @@ -1,31 +1,31 @@ { "info": { - "_postman_id": "3ef1c00f-c58f-4604-8419-7a4931958235", + "_postman_id": "17d62db7-ca12-4298-8782-1d6f018c7be2", "name": "Redirect test", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, "item": [ { "name": "Follow Redirects", + "protocolProfileBehavior": { + "followRedirects": true + }, "request": { "method": "GET", "header": [], "url": { - "raw": "https://mockbin.org/redirect/302/1/?to=https://postman-echo.com/get", + "raw": "https://httpbin.org/redirect-to?url=https://postman-echo.com/get", "protocol": "https", "host": [ - "mockbin", + "httpbin", "org" ], "path": [ - "redirect", - "302", - "1", - "" + "redirect-to" ], "query": [ { - "key": "to", + "key": "url", "value": "https://postman-echo.com/get" } ] @@ -33,6 +33,5 @@ }, "response": [] } - ], - "protocolProfileBehavior": {} + ] } \ No newline at end of file diff --git a/test/codegen/newman/fixtures/unsupportedMethods.json b/test/codegen/newman/fixtures/unsupportedMethods.json new file mode 100644 index 000000000..238ef1dec --- /dev/null +++ b/test/codegen/newman/fixtures/unsupportedMethods.json @@ -0,0 +1,288 @@ +{ + "info": { + "name": "Code-Gen Test PHP Guzzle", + "_postman_id": "43058ce7-94da-0f0f-366e-22b77d566316", + "description": "This collection contains requests that will be used to test validity of plugin created to convert postman request into code snippet of particular language.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "LINK request", + "request": { + "method": "LINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "UNLINK request", + "request": { + "method": "UNLINK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "LOCK request", + "request": { + "method": "LOCK", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "UNLOCK request", + "request": { + "method": "UNLOCK", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PROPFIND request", + "request": { + "method": "PROPFIND", + "header": [ + { + "key": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "mode": "raw", + "raw": "Duis posuere augue vel cursus pharetra. In luctus a ex nec pretium. Praesent neque quam, tincidunt nec leo eget, rutrum vehicula magna.\nMaecenas consequat elementum elit, id semper sem tristique et. Integer pulvinar enim quis consectetur interdum volutpat." + }, + "url": { + "raw": "https://postman-echo.com/request", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ], + "path": [ + "request" + ] + }, + "description": null + }, + "response": [] + }, + { + "name": "PURGE Request", + "request": { + "method": "PURGE", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + }, + "description": "" + }, + "response": [] + }, + { + "name": "COPY Request", + "request": { + "method": "COPY", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + }, + "description": "" + }, + "response": [ + { + "id": "47494bfb-6d21-4f1f-ace0-f88e5e1ac05d", + "name": "COPY Request", + "originalRequest": { + "method": "COPY", + "header": [], + "body": {}, + "url": { + "raw": "https://postman-echo.com", + "protocol": "https", + "host": [ + "postman-echo", + "com" + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "_postman_previewtype": "text", + "header": [ + { + "key": "Access-Control-Allow-Credentials", + "value": "", + "name": "Access-Control-Allow-Credentials", + "description": "Indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials." + }, + { + "key": "Access-Control-Allow-Headers", + "value": "", + "name": "Access-Control-Allow-Headers", + "description": "Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request." + }, + { + "key": "Access-Control-Allow-Methods", + "value": "", + "name": "Access-Control-Allow-Methods", + "description": "Specifies the method or methods allowed when accessing the resource. This is used in response to a preflight request." + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*", + "name": "Access-Control-Allow-Origin", + "description": "Specifies a URI that may access the resource. For requests without credentials, the server may specify '*' as a wildcard, thereby allowing any origin to access the resource." + }, + { + "key": "Access-Control-Expose-Headers", + "value": "", + "name": "Access-Control-Expose-Headers", + "description": "Lets a server whitelist headers that browsers are allowed to access." + }, + { + "key": "Connection", + "value": "keep-alive", + "name": "Connection", + "description": "Options that are desired for the connection" + }, + { + "key": "Content-Encoding", + "value": "gzip", + "name": "Content-Encoding", + "description": "The type of encoding used on the data." + }, + { + "key": "Content-Length", + "value": "59", + "name": "Content-Length", + "description": "The length of the response body in octets (8-bit bytes)" + }, + { + "key": "Content-Type", + "value": "application/json; charset=utf-8", + "name": "Content-Type", + "description": "The mime type of this content" + }, + { + "key": "Date", + "value": "Mon, 05 Feb 2018 07:48:41 GMT", + "name": "Date", + "description": "The date and time that the message was sent" + }, + { + "key": "ETag", + "value": "W/\"af-MmpVeTvfnSW88c4riXD0uw\"", + "name": "ETag", + "description": "An identifier for a specific version of a resource, often a message digest" + }, + { + "key": "Server", + "value": "nginx", + "name": "Server", + "description": "A name for the server" + }, + { + "key": "Vary", + "value": "Accept-Encoding", + "name": "Vary", + "description": "Tells downstream proxies how to match future request headers to decide whether the cached response can be used rather than requesting a fresh one from the origin server." + } + ], + "cookie": [], + "responseTime": 378, + "body": "{\n \"status\": 200,\n \"method\": \"COPY\"\n}" + } + ] + } + ] +} diff --git a/test/codegen/newman/newmanTestUtil.js b/test/codegen/newman/newmanTestUtil.js index 9fa53dc22..26c86673e 100644 --- a/test/codegen/newman/newmanTestUtil.js +++ b/test/codegen/newman/newmanTestUtil.js @@ -1,7 +1,7 @@ var fs = require('fs'), expect = require('chai').expect, exec = require('shelljs').exec, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), path = require('path'), newmanResponses = require('./newmanResponses.json'), async = require('async'); @@ -150,7 +150,7 @@ function runSnippet (testConfig, snippets, collectionName) { }); } } - expect(result[0]).deep.equal(result[1]); + expect(result[0].toString().trim()).deep.equal(result[1].toString().trim()); return done(null); }); }); @@ -179,7 +179,7 @@ module.exports = { // Convert code snippet var collection = require(collectionObj.path); async.map(collection.item, function (item, cb) { - var request = new sdk.Request(item.request); + var request = new Request(item.request); convert(request, options, function (err, snippet) { if (err) { diff --git a/test/codegen/sanity/sanity.test.js b/test/codegen/sanity/sanity.test.js index 7a02e5270..72fd6ad97 100644 --- a/test/codegen/sanity/sanity.test.js +++ b/test/codegen/sanity/sanity.test.js @@ -1,5 +1,5 @@ var expect = require('chai').expect, - sdk = require('postman-collection'), + { Request } = require('postman-collection/lib/collection/request'), path = require('path'), fs = require('fs'), args = process.argv.splice(2), @@ -15,7 +15,7 @@ describe('Sanity tests for ' + codegen, function () { collection = JSON.parse(collection); collection.item.forEach((item) => { - var request = new sdk.Request(item.request); + var request = new Request(item.request); it('should generate snippet for ' + collectionName.split('.')[0] + ' request: ' + item.name, function (done) { converter.convert(request, {}, function (error, snippet) { if (error) { diff --git a/test/codegen/structure.test.js b/test/codegen/structure.test.js index 876494b10..84af756c1 100644 --- a/test/codegen/structure.test.js +++ b/test/codegen/structure.test.js @@ -57,6 +57,12 @@ const expectedOptions = { default: true, description: 'Automatically follow HTTP redirects' }, + followOriginalHttpMethod: { + name: 'Follow original HTTP method', + type: 'boolean', + default: false, + description: 'Redirect with the original HTTP method instead of the default behavior of redirecting with GET' + }, trimRequestBody: { name: 'Trim request body fields', type: 'boolean', @@ -75,12 +81,44 @@ const expectedOptions = { default: false, description: 'Modifies code snippet to incorporate ES6 (EcmaScript) features' }, + asyncAwaitEnabled: { + name: 'Use async/await', + id: 'asyncAwaitEnabled', + type: 'boolean', + default: false, + description: 'Modifies code snippet to use async/await' + }, quoteType: { name: 'Quote Type', type: 'enum', default: 'single', description: 'String denoting the quote type to use (single or double) for URL ' + '(Use double quotes when running curl in cmd.exe and single quotes for the rest)' + }, + maxRedirects: { + name: 'Maximum number of redirects', + type: 'positiveInteger', + default: 0, + description: 'Set the maximum number of redirects to follow, defaults to 0 (unlimited)' + }, + quiet: { + name: 'Use Quiet Mode', + type: 'boolean', + default: false, + description: 'Display the requested data without showing any extra output.' + }, + debug: { + name: 'Use Debug Mode', + type: 'boolean', + default: false, + description: 'Show detailed execution information including retry attempts, redirects, and timing breakdowns.' + }, + lineContinuationCharacter: { + name: 'Line continuation character', + type: 'enum', + default: '\\', + description: 'Set a character used to mark the continuation of a statement on the next line ' + + '(generally, \\ for OSX/Linux, ^ for Windows cmd and ` for Powershell)' } }, // Standard array of ids that should be used for options ids. Any new option should be updated here. @@ -95,12 +133,18 @@ const expectedOptions = { 'silent', 'includeBoilerplate', 'followRedirect', + 'followOriginalHttpMethod', 'lineContinuationCharacter', 'protocol', 'useMimeType', 'ES6_enabled', + 'asyncAwaitEnabled', 'quoteType', - 'asyncType' + 'asyncType', + 'ignoreWarnings', + 'maxRedirects', + 'quiet', + 'debug' ], CODEGEN_ABS_PATH = `./codegens/${codegen}`; describe('Code-gen repository ' + codegen, function () { @@ -135,8 +179,8 @@ describe('Code-gen repository ' + codegen, function () { expect(json.com_postman_plugin).to.have.property('variant'); expect(json.com_postman_plugin).to.have.property('syntax_mode'); expect(json).to.have.property('engines'); - expect(json.engines).to.eql({ - node: '>=8' + expect(json.engines).to.satisfy(function (engines) { + return engines.hasOwnProperty('node') && (engines.node === '>=8' || engines.node === '>=12'); }); }); @@ -152,11 +196,10 @@ describe('Code-gen repository ' + codegen, function () { expect(json.dependencies).to.be.a('object'); }); - it('must point to a valid and precise (no * or ^) semver', function () { - json.dependencies && Object.keys(json.dependencies).forEach(function (item) { - expect(json.dependencies[item]).to.match(new RegExp('^((\\d+)\\.(\\d+)\\.(\\d+))(?:-' + - '([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?$')); // eslint-disable-line max-len - }); + it('should have a valid version string in form of ..', function () { + expect(json.version) + // eslint-disable-next-line max-len, security/detect-unsafe-regex + .to.match(/^((\d+)\.(\d+)\.(\d+))(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$/); }); }); @@ -165,10 +208,11 @@ describe('Code-gen repository ' + codegen, function () { expect(json.devDependencies).to.be.a('object'); }); - it('must point to a valid and precise (no * or ^) semver', function () { - json.devDependencies && Object.keys(json.devDependencies).forEach(function (item) { - expect(json.devDependencies[item]).to.match(new RegExp('^((\\d+)\\.(\\d+)\\.(\\d+))(?:-' + - '([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?$')); // eslint-disable-line max-len + it('should point to a valid semver', function () { + Object.keys(json.devDependencies).forEach(function (dependencyName) { + // eslint-disable-next-line security/detect-non-literal-regexp + expect(json.devDependencies[dependencyName]).to.match(new RegExp('((\\d+)\\.(\\d+)\\.(\\d+))(?:-' + + '([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?$')); }); }); diff --git a/test/system/repository.test.js b/test/system/repository.test.js index c243a570e..9a1e756dc 100644 --- a/test/system/repository.test.js +++ b/test/system/repository.test.js @@ -38,7 +38,7 @@ describe('project repository', function () { expect(json).to.have.property('repository'); expect(json).to.have.property('engines'); - expect(json.engines).to.eql({ node: '>=6' }); + expect(json.engines).to.eql({ node: '>=18' }); }); it('must have a valid version string in form of ..', function () { @@ -53,11 +53,10 @@ describe('project repository', function () { expect(json.dependencies).to.be.a('object'); }); - it('must point to a valid and precise (no * or ^) semver', function () { - json.dependencies && Object.keys(json.dependencies).forEach(function (item) { - expect(json.dependencies[item]).to.match(new RegExp('^((\\d+)\\.(\\d+)\\.(\\d+))(?:-' + - '([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?$')); - }); + it('should have a valid version string in form of ..', function () { + expect(json.version) + // eslint-disable-next-line max-len, security/detect-unsafe-regex + .to.match(/^((\d+)\.(\d+)\.(\d+))(?:-([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?(?:\+([\dA-Za-z-]+(?:\.[\dA-Za-z-]+)*))?$/); }); }); @@ -66,10 +65,11 @@ describe('project repository', function () { expect(json.devDependencies).to.be.a('object'); }); - it('must point to a valid and precise (no * or ^) semver', function () { - json.devDependencies && Object.keys(json.devDependencies).forEach(function (item) { - expect(json.devDependencies[item]).to.match(new RegExp('^((\\d+)\\.(\\d+)\\.(\\d+))(?:-' + - '([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?$')); + it('should point to a valid semver', function () { + Object.keys(json.devDependencies).forEach(function (dependencyName) { + // eslint-disable-next-line security/detect-non-literal-regexp + expect(json.devDependencies[dependencyName]).to.match(new RegExp('((\\d+)\\.(\\d+)\\.(\\d+))(?:-' + + '([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?(?:\\+([\\dA-Za-z\\-]+(?:\\.[\\dA-Za-z\\-]+)*))?$')); }); }); diff --git a/test/unit/lib.test.js b/test/unit/lib.test.js new file mode 100644 index 000000000..77ad8c314 --- /dev/null +++ b/test/unit/lib.test.js @@ -0,0 +1,19 @@ +const expect = require('chai').expect, + lib = require('../../lib'), + labels = require('../../lib/assets/languageLabels.json'); + +describe('lib', function () { + describe('getLanguageList', function () { + it('should test that each language has a valid label', function () { + const list = lib.getLanguageList(); + + expect(list).to.be.an('array'); + + list.forEach(function (lang) { + expect(lang).to.have.property('key'); + expect(lang).to.have.property('label'); + expect(lang.label).to.equal(labels[lang.key]); + }); + }); + }); +});