├── .cargo └── config.toml ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ ├── ci.yaml │ ├── release.yaml │ └── website.yaml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── Cargo.lock ├── Cargo.toml ├── Cross.toml ├── LICENSE ├── README.md ├── ci └── cargo-out-dir ├── cli ├── Cargo.toml ├── README.md ├── build.rs └── src │ └── main.rs ├── justfile ├── src ├── lib.rs ├── openapi │ ├── data │ │ ├── v2 │ │ │ ├── k8s.json │ │ │ ├── petstore-simple.yaml │ │ │ ├── petstore_minimal.yaml │ │ │ ├── rocks.yaml │ │ │ └── uber.yaml │ │ └── v3.0 │ │ │ ├── api-with-examples.yaml │ │ │ ├── callback-example.yaml │ │ │ ├── link-example.yaml │ │ │ ├── petstore-expanded.yaml │ │ │ ├── petstore.yaml │ │ │ └── uspto.yaml │ ├── error.rs │ ├── mod.rs │ └── v3_0 │ │ ├── components.rs │ │ ├── mod.rs │ │ └── schema.rs └── postman │ └── mod.rs ├── tests ├── fixtures │ ├── api-key.postman.json │ ├── calculator-soap.postman.json │ ├── duplicate-query-params.postman.json │ ├── echo.postman.json │ ├── empty-header-object.postman.json │ ├── fastly.postman.json │ ├── github.postman.json │ ├── gotomeeting.postman.json │ ├── graphql.postman.json │ ├── noauth.postman.json │ ├── oauth2-code.postman.json │ ├── only-root-path.postman.json │ ├── pdfco.postman.json │ ├── postman-api.postman.json │ ├── todo.postman.json │ ├── twitter-api.postman.json │ ├── users.postman.json │ └── wasm │ │ ├── collection.json │ │ └── openapi.json ├── integration_tests.rs ├── wasm_browser.rs └── wasm_node.rs ├── web ├── css │ ├── codemirror.css │ └── demo.css ├── js │ ├── collection.json │ ├── index.js │ └── web.js ├── package-lock.json ├── package.json ├── static │ └── index.html └── webpack.config.js └── webdriver.json /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.wasm32-unknown-unknown] 2 | runner = 'wasm-bindgen-test-runner' 3 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/rust:latest 2 | 3 | RUN apt-get update 4 | RUN apt-get install -y ca-certificates curl gnupg 5 | RUN mkdir -p /etc/apt/keyrings 6 | RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg 7 | 8 | RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list 9 | RUN apt-get update && apt-get install nodejs -y 10 | 11 | RUN rustup target install wasm32-unknown-unknown 12 | 13 | RUN cargo install wasm-bindgen-cli 14 | RUN cargo install wasm-opt 15 | RUN cargo install wasm-pack 16 | RUN cargo install just 17 | 18 | RUN chmod -R g+w $CARGO_HOME/registry 19 | 20 | RUN if [ `uname -m` = "x86_64" ]; then \ 21 | apt-get install -y libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 \ 22 | libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 jq && \ 23 | CHROME_STABLE_DOWNLOADS=$(curl https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json | jq -r '.channels.Stable.downloads') && \ 24 | CHROME_URL=$(echo $CHROME_STABLE_DOWNLOADS | jq -r '.chrome[] | select(.platform == "linux64").url') && \ 25 | wget -N "$CHROME_URL" -P ~/ && \ 26 | unzip ~/chrome-linux64.zip -d ~/ && \ 27 | mv ~/chrome-linux64 /opt/chrome && \ 28 | ln -s /opt/chrome/chrome /usr/local/bin/chrome && \ 29 | chmod +x /opt/chrome && \ 30 | rm ~/chrome-linux64.zip && \ 31 | CHROMEDRIVER_URL=$(echo $CHROME_STABLE_DOWNLOADS | jq -r '.chromedriver[] | select(.platform == "linux64").url') && \ 32 | wget -N "$CHROMEDRIVER_URL" -P ~/ && \ 33 | unzip ~/chromedriver-linux64.zip -d ~/ && \ 34 | mv ~/chromedriver-linux64 /opt/chromedriver && \ 35 | ln -s /opt/chromedriver/chromedriver /usr/local/bin/chromedriver && \ 36 | chmod +x /opt/chromedriver && \ 37 | rm ~/chromedriver-linux64.zip; fi 38 | 39 | RUN apt-get update && apt-get install -y firefox-esr && \ 40 | GECKODRIVER_NAME=$(if [ `uname -m` = "aarch64" ]; then echo "geckodriver-v0.33.0-linux-aarch64" ; else echo "geckodriver-v0.33.0-linux64"; fi) && \ 41 | GECKODRIVER_URL="https://github.com/mozilla/geckodriver/releases/download/v0.33.0/$GECKODRIVER_NAME.tar.gz" && \ 42 | wget -N "$GECKODRIVER_URL" -P ~/ && \ 43 | tar -zxf ~/$GECKODRIVER_NAME.tar.gz -C ~/ && \ 44 | mv ~/geckodriver /usr/local/bin/geckodriver && \ 45 | chmod +x /usr/local/bin/geckodriver && \ 46 | rm ~/$GECKODRIVER_NAME.tar.gz 47 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postman2openapi", 3 | "image": "ghcr.io/kevinswiber/postman2openapi-devcontainer", 4 | "postCreateCommand": "rustc --version && echo \"node $(node --version)\" && just --version" 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | schedule: 9 | - cron: "00 00 * * *" 10 | 11 | jobs: 12 | test: 13 | name: test 14 | env: 15 | # For some builds, we use cross to test on 32-bit and big-endian 16 | # systems. 17 | CARGO: cargo 18 | # When CARGO is set to CROSS, this is set to `--target matrix.target`. 19 | TARGET_FLAGS: 20 | # When CARGO is set to CROSS, TARGET_DIR includes matrix.target. 21 | TARGET_DIR: ./target 22 | # Emit backtraces on panics. 23 | RUST_BACKTRACE: 1 24 | 25 | runs-on: ${{ matrix.os }} 26 | 27 | strategy: 28 | matrix: 29 | build: 30 | - stable 31 | - beta 32 | - nightly 33 | - nightly-musl 34 | - nightly-mips 35 | - nightly-32 36 | - macos 37 | - win-msvc 38 | - win-gnu 39 | - msrv 40 | include: 41 | - build: wasm32 42 | os: ubuntu-latest 43 | rust: nightly 44 | - build: msrv 45 | os: ubuntu-latest 46 | rust: 1.64.0 47 | - build: stable 48 | os: ubuntu-latest 49 | rust: stable 50 | - build: beta 51 | os: ubuntu-latest 52 | rust: beta 53 | - build: nightly 54 | os: ubuntu-latest 55 | rust: nightly 56 | - build: nightly-musl 57 | os: ubuntu-latest 58 | rust: nightly 59 | target: x86_64-unknown-linux-musl 60 | - build: nightly-32 61 | os: ubuntu-latest 62 | rust: nightly 63 | target: i686-unknown-linux-gnu 64 | - build: nightly-mips 65 | os: ubuntu-latest 66 | rust: nightly 67 | target: mips64-unknown-linux-gnuabi64 68 | - build: macos 69 | os: macos-latest 70 | rust: nightly 71 | - build: win-msvc 72 | os: windows-2022 73 | rust: nightly 74 | - build: win-gnu 75 | os: windows-2022 76 | rust: nightly-x86_64-gnu 77 | 78 | steps: 79 | - name: Checkout repository 80 | uses: actions/checkout@v2 81 | 82 | - name: Install Rust 83 | id: toolchain 84 | uses: dtolnay/rust-toolchain@master 85 | with: 86 | toolchain: ${{ matrix.rust }} 87 | - run: rustup override set ${{steps.toolchain.outputs.name}} 88 | - run: rustup component add rustfmt clippy 89 | 90 | - name: Use Cross 91 | if: matrix.target != '' && matrix.build != 'wasm32' 92 | run: | 93 | cargo install cross 94 | echo "CARGO=cross" >> $GITHUB_ENV 95 | echo "TARGET_FLAGS=--target ${{ matrix.target }}" >> $GITHUB_ENV 96 | echo "TARGET_DIR=./target/${{ matrix.target }}" >> $GITHUB_ENV 97 | 98 | - name: Show command used for Cargo 99 | run: | 100 | echo "cargo command is: ${{ env.CARGO }}" 101 | echo "target flag is: ${{ env.TARGET_FLAGS }}" 102 | 103 | - name: Build postman2openapi and all crates 104 | run: ${{ env.CARGO }} build --verbose --all ${{ env.TARGET_FLAGS }} 105 | 106 | - name: Run tests 107 | run: ${{ env.CARGO }} test --verbose --all ${{ env.TARGET_FLAGS }} 108 | 109 | - name: Install 110 | if: matrix.build == 'wasm32' 111 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 112 | 113 | - name: Run wasm32 headless Chrome tests 114 | if: matrix.build == 'wasm32' 115 | run: wasm-pack test --headless --chrome 116 | 117 | - name: Run wasm32 Node.js tests 118 | if: matrix.build == 'wasm32' 119 | run: wasm-pack test --node 120 | 121 | rustfmt-clippy: 122 | name: rustfmt 123 | runs-on: ubuntu-latest 124 | steps: 125 | - name: Checkout repository 126 | uses: actions/checkout@v2 127 | - name: Install Rust 128 | id: toolchain 129 | uses: dtolnay/rust-toolchain@stable 130 | - run: rustup component add rustfmt 131 | - name: Check formatting 132 | run: | 133 | cargo fmt --all -- --check 134 | - name: Run clippy 135 | uses: actions-rs/cargo@v1 136 | with: 137 | command: clippy 138 | args: -- -D warnings 139 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | # Stolen from BurntSushi/ripgrep. :D 2 | # 3 | # The way this works is a little weird. But basically, the create-release job 4 | # runs purely to initialize the GitHub release itself. Once done, the upload 5 | # URL of the release is saved as an artifact. 6 | # 7 | # The build-release job runs only once create-release is finished. It gets 8 | # the release upload URL by downloading the corresponding artifact (which was 9 | # uploaded by create-release). It then builds the release executables for each 10 | # supported platform and attaches them as release assets to the previously 11 | # created release. 12 | # 13 | # The key here is that we create the release only once. 14 | 15 | name: release 16 | on: 17 | push: 18 | # Enable when testing release infrastructure on a branch. 19 | # branches: 20 | # - ag/release 21 | tags: 22 | - "[0-9]+.[0-9]+.[0-9]+**" 23 | jobs: 24 | create-release: 25 | name: create-release 26 | runs-on: ubuntu-latest 27 | # env: 28 | # Set to force version number, e.g., when no tag exists. 29 | # POSTMAN2OPENAPI_VERSION: TEST-0.0.0 30 | steps: 31 | - name: Create artifacts directory 32 | run: mkdir artifacts 33 | 34 | - name: Get the release version from the tag 35 | if: env.POSTMAN2OPENAPI_VERSION == '' 36 | run: | 37 | # Apparently, this is the right way to get a tag name. Really? 38 | # 39 | # See: https://github.community/t5/GitHub-Actions/How-to-get-just-the-tag-name/m-p/32167/highlight/true#M1027 40 | echo "POSTMAN2OPENAPI_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV 41 | source $GITHUB_ENV 42 | echo "version is: $POSTMAN2OPENAPI_VERSION" 43 | 44 | - name: Create GitHub release 45 | id: release 46 | uses: actions/create-release@v1 47 | env: 48 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 49 | with: 50 | tag_name: ${{ env.POSTMAN2OPENAPI_VERSION }} 51 | release_name: ${{ env.POSTMAN2OPENAPI_VERSION }} 52 | 53 | - name: Save release upload URL to artifact 54 | run: echo "${{ steps.release.outputs.upload_url }}" > artifacts/release-upload-url 55 | 56 | - name: Save version number to artifact 57 | run: echo "${{ env.POSTMAN2OPENAPI_VERSION }}" > artifacts/release-version 58 | 59 | - name: Upload artifacts 60 | uses: actions/upload-artifact@v1 61 | with: 62 | name: artifacts 63 | path: artifacts 64 | 65 | build-release: 66 | name: build-release 67 | needs: ["create-release"] 68 | runs-on: ${{ matrix.os }} 69 | env: 70 | # For some builds, we use cross to test on 32-bit and big-endian 71 | # systems. 72 | CARGO: cargo 73 | # When CARGO is set to CROSS, this is set to `--target matrix.target`. 74 | TARGET_FLAGS: 75 | # When CARGO is set to CROSS, TARGET_DIR includes matrix.target. 76 | TARGET_DIR: ./target 77 | # Emit backtraces on panics. 78 | RUST_BACKTRACE: 1 79 | strategy: 80 | matrix: 81 | build: [linux, macos, win-msvc, win-gnu, win32-msvc] 82 | include: 83 | - build: linux 84 | os: ubuntu-latest 85 | rust: nightly 86 | target: x86_64-unknown-linux-musl 87 | - build: macos 88 | os: macos-latest 89 | rust: nightly 90 | target: x86_64-apple-darwin 91 | - build: win-msvc 92 | os: windows-2019 93 | rust: nightly 94 | target: x86_64-pc-windows-msvc 95 | - build: win-gnu 96 | os: windows-2019 97 | rust: nightly-x86_64-gnu 98 | target: x86_64-pc-windows-gnu 99 | - build: win32-msvc 100 | os: windows-2019 101 | rust: nightly 102 | target: i686-pc-windows-msvc 103 | 104 | steps: 105 | - name: Checkout repository 106 | uses: actions/checkout@v2 107 | with: 108 | fetch-depth: 1 109 | 110 | # - name: Install packages (Ubuntu) 111 | # if: matrix.os == 'ubuntu-18.04' 112 | # run: | 113 | # ci/ubuntu-install-packages 114 | 115 | # - name: Install packages (macOS) 116 | # if: matrix.os == 'macos-latest' 117 | # run: | 118 | # ci/macos-install-packages 119 | 120 | - name: Install Rust 121 | id: toolchain 122 | uses: dtolnay/rust-toolchain@master 123 | with: 124 | toolchain: ${{ matrix.rust }} 125 | - run: rustup override set ${{steps.toolchain.outputs.name}} 126 | - run: rustup target add ${{ matrix.target }} 127 | 128 | - name: Use Cross 129 | if: matrix.target != '' && matrix.build != 'wasm32' 130 | shell: bash 131 | run: | 132 | # We used to install 'cross' from master, but it kept failing. So now 133 | # we build from a known-good version until 'cross' becomes more stable 134 | # or we find an alternative. Notably, between v0.2.1 and current 135 | # master (2022-06-14), the number of Cross's dependencies has doubled. 136 | cargo install --bins --git https://github.com/rust-embedded/cross --tag v0.2.1 137 | echo "CARGO=cross" >> $GITHUB_ENV 138 | echo "TARGET_FLAGS=--target ${{ matrix.target }}" >> $GITHUB_ENV 139 | echo "TARGET_DIR=./target/${{ matrix.target }}" >> $GITHUB_ENV 140 | 141 | - name: Show command used for Cargo 142 | run: | 143 | echo "cargo command is: ${{ env.CARGO }}" 144 | echo "target flag is: ${{ env.TARGET_FLAGS }}" 145 | echo "target dir is: ${{ env.TARGET_DIR }}" 146 | 147 | - name: Get release download URL 148 | uses: actions/download-artifact@v1 149 | with: 150 | name: artifacts 151 | path: artifacts 152 | 153 | - name: Set release upload URL and release version 154 | shell: bash 155 | run: | 156 | release_upload_url="$(cat artifacts/release-upload-url)" 157 | echo "RELEASE_UPLOAD_URL=$release_upload_url" >> $GITHUB_ENV 158 | release_version="$(cat artifacts/release-version)" 159 | echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV 160 | source $GITHUB_ENV 161 | echo "release upload url: $RELEASE_UPLOAD_URL" 162 | echo "release version: $RELEASE_VERSION" 163 | 164 | - name: Build release binary 165 | run: ${{ env.CARGO }} build --package postman2openapi-cli --verbose --release ${{ env.TARGET_FLAGS }} 166 | 167 | - name: Strip release binary (linux and macos) 168 | if: matrix.build == 'linux' || matrix.build == 'macos' 169 | run: strip "target/${{ matrix.target }}/release/postman2openapi" 170 | 171 | - name: Build archive 172 | shell: bash 173 | run: | 174 | outdir="$(ci/cargo-out-dir "${{ env.TARGET_DIR }}")" 175 | staging="postman2openapi-${{ env.RELEASE_VERSION }}-${{ matrix.target }}" 176 | mkdir "$staging"/ 177 | 178 | cp {README.md,LICENSE} "$staging/" 179 | 180 | if [ "${{ matrix.os }}" = "windows-2019" ]; then 181 | cp "target/${{ matrix.target }}/release/postman2openapi.exe" "$staging/" 182 | 7z a "$staging.zip" "$staging" 183 | echo "ASSET=$staging.zip" >> $GITHUB_ENV 184 | else 185 | # The man page is only generated on Unix systems. ¯\_(ツ)_/¯ 186 | cp "target/${{ matrix.target }}/release/postman2openapi" "$staging/" 187 | tar czf "$staging.tar.gz" "$staging" 188 | echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV 189 | fi 190 | 191 | - name: Upload release archive 192 | uses: actions/upload-release-asset@v1.0.1 193 | env: 194 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 195 | with: 196 | upload_url: ${{ env.RELEASE_UPLOAD_URL }} 197 | asset_path: ${{ env.ASSET }} 198 | asset_name: ${{ env.ASSET }} 199 | asset_content_type: application/octet-stream 200 | -------------------------------------------------------------------------------- /.github/workflows/website.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy postman2openapi Web site to GitHub Pages 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | 25 | - name: Install Rust 26 | id: toolchain 27 | uses: dtolnay/rust-toolchain@1.64.0 28 | - run: rustup override set ${{steps.toolchain.outputs.name}} 29 | - name: Use Node.js 30 | uses: actions/setup-node@v3 31 | with: 32 | node-version: 20.x 33 | 34 | - name: Install wasm-pack 35 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 36 | 37 | - name: Build WebAssembly module 38 | run: wasm-pack build --release --out-dir ./web/wasm --target bundler 39 | 40 | - name: Install dependencies 41 | run: npm install --prefix ./web 42 | 43 | - name: Build 44 | run: npm run build --prefix ./web 45 | 46 | - name: Upload artifact 47 | uses: actions/upload-pages-artifact@v1 48 | with: 49 | path: ./web/dist 50 | 51 | deploy: 52 | environment: 53 | name: github-pages 54 | url: ${{ steps.deployment.outputs.page_url }} 55 | runs-on: ubuntu-latest 56 | needs: build 57 | steps: 58 | - name: Deploy to GitHub Pages 59 | id: deployment 60 | uses: actions/deploy-pages@v1 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nodejs 2 | /target 3 | /web/dist 4 | /web/node_modules 5 | /web/wasm 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug unit tests in library 'postman2openapi'", 11 | "cargo": { 12 | "args": ["test", "--no-run", "--lib", "--package=postman2openapi"], 13 | "filter": { 14 | "name": "postman2openapi", 15 | "kind": "lib" 16 | } 17 | }, 18 | "args": [], 19 | "cwd": "${workspaceFolder}" 20 | }, 21 | { 22 | "type": "lldb", 23 | "request": "launch", 24 | "name": "Debug integration tests", 25 | "cargo": { 26 | "args": ["test", "--test", "integration_tests"], 27 | "filter": { 28 | "name": "integration_tests", 29 | "kind": "test" 30 | } 31 | }, 32 | "args": [], 33 | "cwd": "${workspaceFolder}" 34 | }, 35 | { 36 | "type": "lldb", 37 | "request": "launch", 38 | "name": "Debug executable 'postman2openapi'", 39 | "cargo": { 40 | "args": [ 41 | "build", 42 | "--package=postman2openapi-cli", 43 | "--bin=postman2openapi" 44 | ], 45 | "filter": { 46 | "name": "postman2openapi", 47 | "kind": "bin" 48 | } 49 | }, 50 | "args": [], 51 | "cwd": "${workspaceFolder}" 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/target": true 4 | }, 5 | "lldb.verboseLogging": true, 6 | "[rust]": { 7 | "editor.defaultFormatter": "rust-lang.rust-analyzer", 8 | "editor.formatOnSave": true 9 | }, 10 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "command": "cargo", 6 | "args": ["test", "--test", "integration_tests"], 7 | "problemMatcher": ["$rustc"], 8 | "group": "test", 9 | "label": "rust: cargo test --test integration_tests" 10 | }, 11 | { 12 | "type": "shell", 13 | "command": "wasm-pack", 14 | "args": ["test", "--node", "--test", "wasm_node"], 15 | "problemMatcher": ["$rustc"], 16 | "group": "test", 17 | "label": "rust: wasm-pack test --node" 18 | }, 19 | { 20 | "type": "shell", 21 | "command": "wasm-pack", 22 | "args": ["test", "--headless", "--chrome", "--test", "wasm_browser"], 23 | "problemMatcher": ["$rustc"], 24 | "group": "test", 25 | "label": "rust: wasm-pack test --chrome" 26 | }, 27 | { 28 | "type": "shell", 29 | "command": "wasm-pack", 30 | "args": ["test", "--headless", "--firefox", "--test", "wasm_browser"], 31 | "problemMatcher": ["$rustc"], 32 | "group": "test", 33 | "label": "rust: wasm-pack test --firefox" 34 | }, 35 | { 36 | "command": "cargo", 37 | "args": ["clippy", "--", "-D", "warnings"], 38 | "problemMatcher": ["$rustc"], 39 | "group": "test", 40 | "label": "rust: cargo clippy" 41 | }, 42 | { 43 | "type": "cargo", 44 | "command": "build", 45 | "problemMatcher": ["$rustc"], 46 | "group": { 47 | "kind": "build", 48 | "isDefault": true 49 | }, 50 | "label": "rust: cargo build" 51 | }, 52 | { 53 | "type": "cargo", 54 | "command": "test", 55 | "problemMatcher": ["$rustc"], 56 | "group": { 57 | "kind": "test", 58 | "isDefault": true 59 | }, 60 | "label": "rust: cargo test" 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.0.5" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "android-tzdata" 16 | version = "0.1.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 19 | 20 | [[package]] 21 | name = "android_system_properties" 22 | version = "0.1.5" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 25 | dependencies = [ 26 | "libc", 27 | ] 28 | 29 | [[package]] 30 | name = "anyhow" 31 | version = "1.0.75" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" 34 | 35 | [[package]] 36 | name = "atty" 37 | version = "0.2.14" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 40 | dependencies = [ 41 | "hermit-abi", 42 | "libc", 43 | "winapi", 44 | ] 45 | 46 | [[package]] 47 | name = "autocfg" 48 | version = "1.1.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 51 | 52 | [[package]] 53 | name = "bitflags" 54 | version = "1.3.2" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 57 | 58 | [[package]] 59 | name = "bumpalo" 60 | version = "3.13.0" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" 63 | 64 | [[package]] 65 | name = "cc" 66 | version = "1.0.83" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 69 | dependencies = [ 70 | "libc", 71 | ] 72 | 73 | [[package]] 74 | name = "cfg-if" 75 | version = "0.1.10" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 78 | 79 | [[package]] 80 | name = "cfg-if" 81 | version = "1.0.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 84 | 85 | [[package]] 86 | name = "chrono" 87 | version = "0.4.30" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" 90 | dependencies = [ 91 | "android-tzdata", 92 | "iana-time-zone", 93 | "js-sys", 94 | "num-traits", 95 | "wasm-bindgen", 96 | "windows-targets", 97 | ] 98 | 99 | [[package]] 100 | name = "clap" 101 | version = "3.2.25" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" 104 | dependencies = [ 105 | "atty", 106 | "bitflags", 107 | "clap_lex", 108 | "indexmap 1.9.3", 109 | "once_cell", 110 | "strsim", 111 | "termcolor", 112 | "textwrap", 113 | ] 114 | 115 | [[package]] 116 | name = "clap_lex" 117 | version = "0.2.4" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 120 | dependencies = [ 121 | "os_str_bytes", 122 | ] 123 | 124 | [[package]] 125 | name = "console_error_panic_hook" 126 | version = "0.1.7" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" 129 | dependencies = [ 130 | "cfg-if 1.0.0", 131 | "wasm-bindgen", 132 | ] 133 | 134 | [[package]] 135 | name = "convert_case" 136 | version = "0.5.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" 139 | 140 | [[package]] 141 | name = "core-foundation-sys" 142 | version = "0.8.4" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" 145 | 146 | [[package]] 147 | name = "equivalent" 148 | version = "1.0.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 151 | 152 | [[package]] 153 | name = "gloo-utils" 154 | version = "0.2.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" 157 | dependencies = [ 158 | "js-sys", 159 | "serde", 160 | "serde_json", 161 | "wasm-bindgen", 162 | "web-sys", 163 | ] 164 | 165 | [[package]] 166 | name = "hashbrown" 167 | version = "0.12.3" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 170 | 171 | [[package]] 172 | name = "hashbrown" 173 | version = "0.14.0" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" 176 | 177 | [[package]] 178 | name = "hermit-abi" 179 | version = "0.1.19" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 182 | dependencies = [ 183 | "libc", 184 | ] 185 | 186 | [[package]] 187 | name = "iana-time-zone" 188 | version = "0.1.57" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" 191 | dependencies = [ 192 | "android_system_properties", 193 | "core-foundation-sys", 194 | "iana-time-zone-haiku", 195 | "js-sys", 196 | "wasm-bindgen", 197 | "windows", 198 | ] 199 | 200 | [[package]] 201 | name = "iana-time-zone-haiku" 202 | version = "0.1.2" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 205 | dependencies = [ 206 | "cc", 207 | ] 208 | 209 | [[package]] 210 | name = "indexmap" 211 | version = "1.9.3" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 214 | dependencies = [ 215 | "autocfg", 216 | "hashbrown 0.12.3", 217 | "serde", 218 | ] 219 | 220 | [[package]] 221 | name = "indexmap" 222 | version = "2.0.0" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" 225 | dependencies = [ 226 | "equivalent", 227 | "hashbrown 0.14.0", 228 | ] 229 | 230 | [[package]] 231 | name = "itoa" 232 | version = "1.0.9" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" 235 | 236 | [[package]] 237 | name = "js-sys" 238 | version = "0.3.64" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" 241 | dependencies = [ 242 | "wasm-bindgen", 243 | ] 244 | 245 | [[package]] 246 | name = "lazy_static" 247 | version = "1.4.0" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 250 | 251 | [[package]] 252 | name = "libc" 253 | version = "0.2.147" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 256 | 257 | [[package]] 258 | name = "log" 259 | version = "0.4.20" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 262 | 263 | [[package]] 264 | name = "memchr" 265 | version = "2.6.3" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" 268 | 269 | [[package]] 270 | name = "memory_units" 271 | version = "0.4.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" 274 | 275 | [[package]] 276 | name = "num-traits" 277 | version = "0.2.16" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" 280 | dependencies = [ 281 | "autocfg", 282 | ] 283 | 284 | [[package]] 285 | name = "once_cell" 286 | version = "1.18.0" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" 289 | 290 | [[package]] 291 | name = "os_str_bytes" 292 | version = "6.5.1" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" 295 | 296 | [[package]] 297 | name = "postman2openapi" 298 | version = "1.2.1" 299 | dependencies = [ 300 | "anyhow", 301 | "console_error_panic_hook", 302 | "convert_case", 303 | "gloo-utils", 304 | "indexmap 1.9.3", 305 | "js-sys", 306 | "lazy_static", 307 | "regex", 308 | "semver", 309 | "serde", 310 | "serde_derive", 311 | "serde_json", 312 | "serde_yaml", 313 | "thiserror", 314 | "wasm-bindgen", 315 | "wasm-bindgen-test", 316 | "wee_alloc", 317 | ] 318 | 319 | [[package]] 320 | name = "postman2openapi-cli" 321 | version = "1.2.1" 322 | dependencies = [ 323 | "atty", 324 | "chrono", 325 | "clap", 326 | "lazy_static", 327 | "postman2openapi", 328 | ] 329 | 330 | [[package]] 331 | name = "proc-macro2" 332 | version = "1.0.66" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" 335 | dependencies = [ 336 | "unicode-ident", 337 | ] 338 | 339 | [[package]] 340 | name = "quote" 341 | version = "1.0.33" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 344 | dependencies = [ 345 | "proc-macro2", 346 | ] 347 | 348 | [[package]] 349 | name = "regex" 350 | version = "1.9.5" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" 353 | dependencies = [ 354 | "aho-corasick", 355 | "memchr", 356 | "regex-automata", 357 | "regex-syntax", 358 | ] 359 | 360 | [[package]] 361 | name = "regex-automata" 362 | version = "0.3.8" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" 365 | dependencies = [ 366 | "aho-corasick", 367 | "memchr", 368 | "regex-syntax", 369 | ] 370 | 371 | [[package]] 372 | name = "regex-syntax" 373 | version = "0.7.5" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" 376 | 377 | [[package]] 378 | name = "ryu" 379 | version = "1.0.15" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 382 | 383 | [[package]] 384 | name = "scoped-tls" 385 | version = "1.0.1" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 388 | 389 | [[package]] 390 | name = "semver" 391 | version = "1.0.18" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" 394 | 395 | [[package]] 396 | name = "serde" 397 | version = "1.0.188" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" 400 | dependencies = [ 401 | "serde_derive", 402 | ] 403 | 404 | [[package]] 405 | name = "serde_derive" 406 | version = "1.0.188" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" 409 | dependencies = [ 410 | "proc-macro2", 411 | "quote", 412 | "syn", 413 | ] 414 | 415 | [[package]] 416 | name = "serde_json" 417 | version = "1.0.106" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" 420 | dependencies = [ 421 | "itoa", 422 | "ryu", 423 | "serde", 424 | ] 425 | 426 | [[package]] 427 | name = "serde_yaml" 428 | version = "0.9.25" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" 431 | dependencies = [ 432 | "indexmap 2.0.0", 433 | "itoa", 434 | "ryu", 435 | "serde", 436 | "unsafe-libyaml", 437 | ] 438 | 439 | [[package]] 440 | name = "strsim" 441 | version = "0.10.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 444 | 445 | [[package]] 446 | name = "syn" 447 | version = "2.0.32" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" 450 | dependencies = [ 451 | "proc-macro2", 452 | "quote", 453 | "unicode-ident", 454 | ] 455 | 456 | [[package]] 457 | name = "termcolor" 458 | version = "1.2.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 461 | dependencies = [ 462 | "winapi-util", 463 | ] 464 | 465 | [[package]] 466 | name = "textwrap" 467 | version = "0.16.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" 470 | 471 | [[package]] 472 | name = "thiserror" 473 | version = "1.0.48" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" 476 | dependencies = [ 477 | "thiserror-impl", 478 | ] 479 | 480 | [[package]] 481 | name = "thiserror-impl" 482 | version = "1.0.48" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" 485 | dependencies = [ 486 | "proc-macro2", 487 | "quote", 488 | "syn", 489 | ] 490 | 491 | [[package]] 492 | name = "unicode-ident" 493 | version = "1.0.11" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" 496 | 497 | [[package]] 498 | name = "unsafe-libyaml" 499 | version = "0.2.9" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" 502 | 503 | [[package]] 504 | name = "wasm-bindgen" 505 | version = "0.2.87" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" 508 | dependencies = [ 509 | "cfg-if 1.0.0", 510 | "wasm-bindgen-macro", 511 | ] 512 | 513 | [[package]] 514 | name = "wasm-bindgen-backend" 515 | version = "0.2.87" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" 518 | dependencies = [ 519 | "bumpalo", 520 | "log", 521 | "once_cell", 522 | "proc-macro2", 523 | "quote", 524 | "syn", 525 | "wasm-bindgen-shared", 526 | ] 527 | 528 | [[package]] 529 | name = "wasm-bindgen-futures" 530 | version = "0.4.37" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" 533 | dependencies = [ 534 | "cfg-if 1.0.0", 535 | "js-sys", 536 | "wasm-bindgen", 537 | "web-sys", 538 | ] 539 | 540 | [[package]] 541 | name = "wasm-bindgen-macro" 542 | version = "0.2.87" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" 545 | dependencies = [ 546 | "quote", 547 | "wasm-bindgen-macro-support", 548 | ] 549 | 550 | [[package]] 551 | name = "wasm-bindgen-macro-support" 552 | version = "0.2.87" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" 555 | dependencies = [ 556 | "proc-macro2", 557 | "quote", 558 | "syn", 559 | "wasm-bindgen-backend", 560 | "wasm-bindgen-shared", 561 | ] 562 | 563 | [[package]] 564 | name = "wasm-bindgen-shared" 565 | version = "0.2.87" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" 568 | 569 | [[package]] 570 | name = "wasm-bindgen-test" 571 | version = "0.3.37" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" 574 | dependencies = [ 575 | "console_error_panic_hook", 576 | "js-sys", 577 | "scoped-tls", 578 | "wasm-bindgen", 579 | "wasm-bindgen-futures", 580 | "wasm-bindgen-test-macro", 581 | ] 582 | 583 | [[package]] 584 | name = "wasm-bindgen-test-macro" 585 | version = "0.3.37" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" 588 | dependencies = [ 589 | "proc-macro2", 590 | "quote", 591 | ] 592 | 593 | [[package]] 594 | name = "web-sys" 595 | version = "0.3.64" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" 598 | dependencies = [ 599 | "js-sys", 600 | "wasm-bindgen", 601 | ] 602 | 603 | [[package]] 604 | name = "wee_alloc" 605 | version = "0.4.5" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" 608 | dependencies = [ 609 | "cfg-if 0.1.10", 610 | "libc", 611 | "memory_units", 612 | "winapi", 613 | ] 614 | 615 | [[package]] 616 | name = "winapi" 617 | version = "0.3.9" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 620 | dependencies = [ 621 | "winapi-i686-pc-windows-gnu", 622 | "winapi-x86_64-pc-windows-gnu", 623 | ] 624 | 625 | [[package]] 626 | name = "winapi-i686-pc-windows-gnu" 627 | version = "0.4.0" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 630 | 631 | [[package]] 632 | name = "winapi-util" 633 | version = "0.1.5" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 636 | dependencies = [ 637 | "winapi", 638 | ] 639 | 640 | [[package]] 641 | name = "winapi-x86_64-pc-windows-gnu" 642 | version = "0.4.0" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 645 | 646 | [[package]] 647 | name = "windows" 648 | version = "0.48.0" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" 651 | dependencies = [ 652 | "windows-targets", 653 | ] 654 | 655 | [[package]] 656 | name = "windows-targets" 657 | version = "0.48.5" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 660 | dependencies = [ 661 | "windows_aarch64_gnullvm", 662 | "windows_aarch64_msvc", 663 | "windows_i686_gnu", 664 | "windows_i686_msvc", 665 | "windows_x86_64_gnu", 666 | "windows_x86_64_gnullvm", 667 | "windows_x86_64_msvc", 668 | ] 669 | 670 | [[package]] 671 | name = "windows_aarch64_gnullvm" 672 | version = "0.48.5" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 675 | 676 | [[package]] 677 | name = "windows_aarch64_msvc" 678 | version = "0.48.5" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 681 | 682 | [[package]] 683 | name = "windows_i686_gnu" 684 | version = "0.48.5" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 687 | 688 | [[package]] 689 | name = "windows_i686_msvc" 690 | version = "0.48.5" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 693 | 694 | [[package]] 695 | name = "windows_x86_64_gnu" 696 | version = "0.48.5" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 699 | 700 | [[package]] 701 | name = "windows_x86_64_gnullvm" 702 | version = "0.48.5" 703 | source = "registry+https://github.com/rust-lang/crates.io-index" 704 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 705 | 706 | [[package]] 707 | name = "windows_x86_64_msvc" 708 | version = "0.48.5" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 711 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "postman2openapi" 3 | description = "Convert a Postman collection to an OpenAPI definition. " 4 | homepage = "https://github.com/kevinswiber/postman2openapi" 5 | repository = "https://github.com/kevinswiber/postman2openapi" 6 | license = "Apache-2.0" 7 | version = "1.2.1" 8 | authors = ["Kevin Swiber "] 9 | readme = "README.md" 10 | documentation = "https://docs.rs/postman2openapi" 11 | edition = "2021" 12 | exclude = ["ci/*", "tests/*", "web/*"] 13 | 14 | [workspace] 15 | members = ["cli"] 16 | 17 | [lib] 18 | name = "postman2openapi" 19 | path = "src/lib.rs" 20 | crate-type = ["cdylib", "rlib"] 21 | 22 | [dependencies] 23 | anyhow = "1.0" 24 | convert_case = "0.5.0" 25 | indexmap = { version = "1.5.1", features = ["serde-1"] } 26 | lazy_static = "1.4.0" 27 | regex = { version = "1.6", default-features = false, features = ["std"] } 28 | semver = "1.0.12" 29 | serde = "1.0" 30 | serde_derive = "1.0" 31 | serde_json = { version = "1.0", features = ["raw_value"]} 32 | thiserror = "1.0" 33 | 34 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 35 | serde_yaml = "0.9" 36 | 37 | [target.'cfg(target_arch = "wasm32")'.dependencies] 38 | console_error_panic_hook = { version = "0.1.6", optional = true } 39 | gloo-utils = { version = "0.2", features = ["serde"] } 40 | wasm-bindgen = "0.2" 41 | wee_alloc = { version = "0.4.5", optional = true } 42 | 43 | [target.'cfg(target_arch = "wasm32")'.dev-dependencies] 44 | js-sys = "0.3" 45 | wasm-bindgen-test = "0.3.0" 46 | 47 | [profile.release] 48 | opt-level = "z" 49 | lto = true 50 | 51 | [package.metadata.wasm-pack.profile.release] 52 | wasm-opt = ["-Oz", "--enable-mutable-globals"] 53 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | 2 | [target.x86_64-unknown-linux-musl] 3 | image = "burntsushi/cross:x86_64-unknown-linux-musl" 4 | 5 | [target.i686-unknown-linux-gnu] 6 | image = "burntsushi/cross:i686-unknown-linux-gnu" 7 | 8 | [target.mips64-unknown-linux-gnuabi64] 9 | image = "burntsushi/cross:mips64-unknown-linux-gnuabi64" 10 | build-std = true 11 | 12 | [target.arm-unknown-linux-gnueabihf] 13 | image = "burntsushi/cross:arm-unknown-linux-gnueabihf" 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # postman2openapi 2 | 3 | Convert Postman collections to OpenAPI definitions. 4 | 5 | [![Build status](https://github.com/kevinswiber/postman2openapi/workflows/ci/badge.svg)](https://github.com/kevinswiber/postman2openapi/actions) 6 | 7 | **Try it on the Web: https://kevinswiber.github.io/postman2openapi/** 8 | 9 | - [CLI](#cli) 10 | - [Installation](#installation) 11 | - [Usage](#usage) 12 | - [Examples](#examples) 13 | - [JavaScript Library](#javascript-library) 14 | - [Installation](#installation-1) 15 | - [Usage](#usage-1) 16 | - [JavaScript API](#javascript-api) 17 | - [Build](#build) 18 | - [License](#license) 19 | 20 | ## CLI 21 | 22 | ### Installation 23 | 24 | [Archives of precompiled binaries for postman2openapi are available for Windows, 25 | macOS and Linux.](https://github.com/kevinswiber/postman2openapi/releases) 26 | 27 | Linux binaries are static executables. Windows binaries are available either as 28 | built with MinGW (GNU) or with Microsoft Visual C++ (MSVC). When possible, 29 | prefer MSVC over GNU, but you'll need to have the [Microsoft VC++ 2015 30 | redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=48145) 31 | installed. 32 | 33 | For Rust developers, installation is also available via Cargo. [Installing Rust and Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) 34 | 35 | To install the latest published version on crates.io, use: 36 | 37 | ``` 38 | cargo install postman2openapi-cli 39 | ``` 40 | 41 | To install from the latest on GitHub, use: 42 | 43 | ``` 44 | cargo install --git https://github.com/kevinswiber/postman2openapi postman2openapi-cli 45 | ``` 46 | 47 | ### Usage 48 | 49 | ``` 50 | USAGE: 51 | postman2openapi [OPTIONS] [input-file] 52 | 53 | ARGS: 54 | The Postman collection to convert; data may also come from stdin 55 | 56 | OPTIONS: 57 | -f, --output-format The output format [default: yaml] [possible values: yaml, json] 58 | -h, --help Print help information 59 | -V, --version Print version information 60 | ``` 61 | 62 | #### Examples 63 | 64 | ``` 65 | postman2openapi collection.json > openapi.yaml 66 | ``` 67 | 68 | ``` 69 | cat collection.json | postman2openapi -f json 70 | ``` 71 | 72 | ## JavaScript library 73 | 74 | ### Installation 75 | 76 | ``` 77 | npm install postman2openapi 78 | ``` 79 | 80 | ### Usage 81 | 82 | ```js 83 | const collection = require("./collection"); // any Postman collection JSON file 84 | const { transpile } = require("postman2openapi"); 85 | 86 | // Returns a JavaScript object representation of the OpenAPI definition. 87 | const openapi = transpile(collection); 88 | 89 | console.log(JSON.stringify(openapi, null, 2)); 90 | ``` 91 | 92 | ### JavaScript API 93 | 94 | #### transpile(collection: object): object 95 | 96 | - collection - An object representing the Postman collection. 97 | - _returns_ - an OpenAPI definition as a JavaScript object. 98 | 99 | ## Build 100 | 101 | Note: A [Dev Container](https://containers.dev/) is included for convenience. 102 | 103 | To take advantage of build recipes, install [just](https://github.com/casey/just#packages). 104 | 105 | ### `just build` 106 | 107 | Builds the Rust library and the CLI packages. 108 | 109 | ### `just test` 110 | 111 | Runs all lint checks (`cargo fmt --check`, `cargo clippy`) and runs all tests, including tests for `wasm32-unknown-unknown` targets (Node.js, Chrome, Firefox). 112 | 113 | ### `just start-web` 114 | 115 | Builds the WebAssembly project and starts a local version of the [postman2openapi site](https://kevinswiber.github.io/postman2openapi/). 116 | 117 | ### `just prepare` 118 | 119 | Builds the Rust library, the CLI, the Node.js library, and the Web site. Then all tests are run. 120 | 121 | ## License 122 | 123 | Apache License 2.0 (Apache-2.0) 124 | -------------------------------------------------------------------------------- /ci/cargo-out-dir: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Finds Cargo's `OUT_DIR` directory from the most recent build. 4 | # 5 | # This requires one parameter corresponding to the target directory 6 | # to search for the build output. 7 | 8 | if [ $# != 1 ]; then 9 | echo "Usage: $(basename "$0") " >&2 10 | exit 2 11 | fi 12 | 13 | # This works by finding the most recent stamp file, which is produced by 14 | # every postman2openapi build. 15 | target_dir="$1" 16 | find "$target_dir" -name postman2openapi-stamp -print0 \ 17 | | xargs -0 ls -t \ 18 | | head -n1 \ 19 | | xargs dirname 20 | -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "postman2openapi-cli" 3 | authors = ["Kevin Swiber "] 4 | version = "1.2.1" 5 | description = "Convert a Postman collection to an OpenAPI definition. " 6 | homepage = "https://github.com/kevinswiber/postman2openapi" 7 | repository = "https://github.com/kevinswiber/postman2openapi" 8 | license = "Apache-2.0" 9 | readme = "README.md" 10 | documentation = "https://docs.rs/postman2openapi" 11 | build = "build.rs" 12 | edition = "2021" 13 | 14 | [[bin]] 15 | name = "postman2openapi" 16 | path = "src/main.rs" 17 | 18 | [build-dependencies] 19 | chrono = "0.4" 20 | 21 | [dependencies] 22 | atty = "0.2" 23 | clap = { version = "3.2", features = ["cargo"] } 24 | lazy_static = "1.4.0" 25 | postman2openapi = { path = "../", version = "1.1.0" } 26 | -------------------------------------------------------------------------------- /cli/README.md: -------------------------------------------------------------------------------- 1 | # postman2openapi-cli 2 | 3 | Convert Postman collections to OpenAPI definitions. 4 | 5 | ## Installation 6 | 7 | [Archives of precompiled binaries for postman2openapi are available for Windows, 8 | macOS and Linux.](https://github.com/kevinswiber/postman2openapi/releases) 9 | 10 | Linux binaries are static executables. Windows binaries are available either as 11 | built with MinGW (GNU) or with Microsoft Visual C++ (MSVC). When possible, 12 | prefer MSVC over GNU, but you'll need to have the [Microsoft VC++ 2015 13 | redistributable](https://www.microsoft.com/en-us/download/details.aspx?id=48145) 14 | installed. 15 | 16 | For Rust developers, installation is also available via Cargo. [Installing Rust and Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) 17 | 18 | To install the latest published version on crates.io, use: 19 | 20 | ``` 21 | cargo install postman2openapi-cli 22 | ``` 23 | 24 | To install from the latest on GitHub, use: 25 | 26 | ``` 27 | cargo install --git https://github.com/kevinswiber/postman2openapi postman2openapi-cli 28 | ``` 29 | 30 | ## Usage 31 | 32 | ``` 33 | USAGE: 34 | postman2openapi [OPTIONS] [input-file] 35 | 36 | ARGS: 37 | The Postman collection to convert; data may also come from stdin 38 | 39 | OPTIONS: 40 | -f, --output-format The output format [default: yaml] [possible values: yaml, json] 41 | -h, --help Print help information 42 | -V, --version Print version information 43 | ``` 44 | 45 | #### Examples 46 | 47 | ``` 48 | postman2openapi collection.json > openapi.yaml 49 | ``` 50 | 51 | ``` 52 | cat collection.json | postman2openapi -f json 53 | ``` 54 | 55 | ## License 56 | 57 | Apache License 2.0 (Apache-2.0) 58 | -------------------------------------------------------------------------------- /cli/build.rs: -------------------------------------------------------------------------------- 1 | extern crate chrono; 2 | 3 | use chrono::prelude::*; 4 | use std::env; 5 | use std::fs::{self, File}; 6 | use std::path::Path; 7 | use std::process; 8 | 9 | fn main() { 10 | // OUT_DIR is set by Cargo and it's where any additional build artifacts 11 | // are written. 12 | let outdir = match env::var_os("OUT_DIR") { 13 | Some(outdir) => outdir, 14 | None => { 15 | eprintln!( 16 | "OUT_DIR environment variable not defined. \ 17 | Please file a bug: \ 18 | https://github.com/kevinswiber/postman2openapi/issues/new" 19 | ); 20 | process::exit(1); 21 | } 22 | }; 23 | fs::create_dir_all(&outdir).unwrap(); 24 | 25 | let stamp_path = Path::new(&outdir).join("postman2openapi-stamp"); 26 | if let Err(err) = File::create(&stamp_path) { 27 | panic!("failed to write {}: {}", stamp_path.display(), err); 28 | } 29 | // Make the current git hash available to the build. 30 | if let Some(rev) = git_revision_hash() { 31 | println!("cargo:rustc-env=POSTMAN2OPENAPI_BUILD_GIT_HASH={rev}"); 32 | } 33 | if let Some(branch) = git_branch() { 34 | println!("cargo:rustc-env=POSTMAN2OPENAPI_BUILD_GIT_BRANCH={branch}"); 35 | } 36 | let date = Utc::now().format("%Y-%m-%dT%H:%M:%SZ"); 37 | println!("cargo:rustc-env=POSTMAN2OPENAPI_BUILD_DATE={date}"); 38 | } 39 | 40 | fn git_revision_hash() -> Option { 41 | let result = process::Command::new("git") 42 | .args(["rev-parse", "--short=10", "HEAD"]) 43 | .output(); 44 | result.ok().and_then(|output| { 45 | let v = String::from_utf8_lossy(&output.stdout).trim().to_string(); 46 | if v.is_empty() { 47 | None 48 | } else { 49 | Some(v) 50 | } 51 | }) 52 | } 53 | 54 | fn git_branch() -> Option { 55 | let result = process::Command::new("git") 56 | .args(["rev-parse", "--abbrev-ref", "HEAD"]) 57 | .output(); 58 | result.ok().and_then(|output| { 59 | let v = String::from_utf8_lossy(&output.stdout).trim().to_string(); 60 | if v.is_empty() { 61 | None 62 | } else { 63 | Some(v) 64 | } 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{crate_authors, crate_version, App, AppSettings, Arg}; 2 | use lazy_static::lazy_static; 3 | use postman2openapi::{from_path, from_str, TranspileOptions}; 4 | use std::io::{stdin, Read}; 5 | 6 | fn main() { 7 | let authors = crate_authors!("\n"); 8 | let version = match option_env!("POSTMAN2OPENAPI_VERSION") { 9 | None => format!("v{}-dev", crate_version!()), 10 | Some(version) => format!("v{}", version), 11 | }; 12 | 13 | lazy_static! { 14 | static ref LONG_VERSION: String = long_version(); 15 | } 16 | 17 | let mut app = App::new("postman2openapi") 18 | .version(version.as_str()) 19 | .long_version(LONG_VERSION.as_str()) 20 | .author(authors) 21 | .setting(AppSettings::ColoredHelp) 22 | .arg( 23 | Arg::new("output-format") 24 | .short('f') 25 | .long("output-format") 26 | .help("The output format") 27 | .value_name("format") 28 | .possible_values(&["yaml", "json"]) 29 | .default_value("yaml"), 30 | ) 31 | .arg( 32 | Arg::new("INPUT") 33 | .value_name("input-file") 34 | .help("The Postman collection to convert; data may also come from stdin") 35 | .index(1), 36 | ); 37 | 38 | if std::env::args().len() < 2 && atty::is(atty::Stream::Stdin) { 39 | let _ = app.print_help(); 40 | return; 41 | } 42 | 43 | let matches = app.get_matches(); 44 | 45 | let mut buffer = String::new(); 46 | let format = matches 47 | .value_of_t("output-format") 48 | .unwrap_or_else(|e| e.exit()); 49 | match &matches.value_of("INPUT") { 50 | Some(filename) => match from_path(filename, TranspileOptions { format }) { 51 | Ok(oas) => println!("{}", oas), 52 | Err(err) => eprintln!("{}", err), 53 | }, 54 | None => match stdin().read_to_string(&mut buffer) { 55 | Ok(_) => match from_str(&buffer, TranspileOptions { format }) { 56 | Ok(oas) => println!("{}", oas), 57 | Err(err) => eprintln!("{}", err), 58 | }, 59 | Err(_) => eprintln!("postman2openapi: warning: recursive search of stdin"), 60 | }, 61 | }; 62 | } 63 | 64 | pub fn long_version() -> String { 65 | let hash = match option_env!("POSTMAN2OPENAPI_BUILD_GIT_HASH") { 66 | None => String::new(), 67 | Some(hash) => hash.to_string(), 68 | }; 69 | 70 | let branch = match option_env!("POSTMAN2OPENAPI_BUILD_GIT_BRANCH") { 71 | None => String::new(), 72 | Some(branch) => branch.to_string(), 73 | }; 74 | 75 | let date = match option_env!("POSTMAN2OPENAPI_BUILD_DATE") { 76 | None => String::new(), 77 | Some(date) => date.to_string(), 78 | }; 79 | 80 | let version = match option_env!("POSTMAN2OPENAPI_VERSION") { 81 | None => format!("v{}-dev.{}+{}", crate_version!(), branch, hash), 82 | Some(version) => format!("v{}", version), 83 | }; 84 | 85 | format!("{}\ncommit: {}\ndate: {}\n", version, hash, date) 86 | } 87 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | prepare: build-release build-nodejs build-web test 2 | 3 | build: 4 | cargo build --all 5 | 6 | build-release: 7 | cargo build --release --all 8 | 9 | start-web: build-web 10 | npm run start --prefix ./web 11 | 12 | build-lib: 13 | cargo build 14 | build-cli: 15 | cargo build --package postman2openapi-cli 16 | build-web: 17 | wasm-pack build --release --out-dir ./web/wasm --target bundler 18 | npm install --prefix ./web 19 | npm run build --prefix ./web 20 | build-nodejs: 21 | wasm-pack build --release --out-dir ./nodejs --target nodejs 22 | build-devcontainer-image: 23 | NEEDS_BUILDER=$(docker buildx ls | grep -q postman2openapi; echo $?); \ 24 | if [[ "$NEEDS_BUILDER" = "1" ]]; then docker buildx create --name postman2openapi --bootstrap --use; \ 25 | else docker buildx use postman2openapi; fi && \ 26 | docker buildx build --platform linux/amd64,linux/arm64 --push -f ./.devcontainer/Dockerfile -t ghcr.io/kevinswiber/postman2openapi-devcontainer:latest . 27 | 28 | push-devcontainer-image: 29 | docker push ghcr.io/kevinswiber/postman2openapi-devcontainer:latest 30 | 31 | fmt-check: 32 | cargo fmt --check --all 33 | clippy: 34 | cargo clippy -- -D warnings 35 | 36 | test: build fmt-check clippy test-lib test-integration test-wasm-node test-wasm-chrome test-wasm-firefox 37 | 38 | test-lib: 39 | cargo test --lib 40 | test-integration: 41 | cargo test --test integration_tests 42 | test-wasm-firefox: 43 | (which geckodriver && wasm-pack test --headless --firefox --test wasm_browser) || echo "Install geckodriver to run Firefox tests." 44 | test-wasm-chrome: 45 | (which chromedriver && wasm-pack test --headless --chrome --test wasm_browser) || echo "Install chromedriver to run Chrome tests." 46 | test-wasm-node: 47 | wasm-pack test --node --test wasm_node 48 | -------------------------------------------------------------------------------- /src/openapi/data/v2/petstore-simple.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | swagger: "2.0" 3 | info: 4 | version: "1.0.0" 5 | title: "Swagger Petstore" 6 | description: "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification" 7 | termsOfService: "http://swagger.io/terms/" 8 | contact: 9 | name: "Swagger API Team" 10 | license: 11 | name: "MIT" 12 | host: "petstore.swagger.io" 13 | basePath: "/api" 14 | schemes: 15 | - "http" 16 | consumes: 17 | - "application/json" 18 | produces: 19 | - "application/json" 20 | paths: 21 | /pets: 22 | get: 23 | description: "Returns all pets from the system that the user has access to" 24 | operationId: "findPets" 25 | produces: 26 | - "application/json" 27 | - "application/xml" 28 | - "text/xml" 29 | - "text/html" 30 | parameters: 31 | - 32 | name: "tags" 33 | in: "query" 34 | description: "tags to filter by" 35 | required: false 36 | type: "array" 37 | items: 38 | type: "string" 39 | collectionFormat: "csv" 40 | - 41 | name: "limit" 42 | in: "query" 43 | description: "maximum number of results to return" 44 | required: false 45 | type: "integer" 46 | format: "int32" 47 | responses: 48 | "200": 49 | description: "pet response" 50 | schema: 51 | type: "array" 52 | items: 53 | $ref: "#/definitions/Pet" 54 | default: 55 | description: "unexpected error" 56 | schema: 57 | $ref: "#/definitions/ErrorModel" 58 | post: 59 | description: "Creates a new pet in the store. Duplicates are allowed" 60 | operationId: "addPet" 61 | produces: 62 | - "application/json" 63 | parameters: 64 | - 65 | name: "pet" 66 | in: "body" 67 | description: "Pet to add to the store" 68 | required: true 69 | schema: 70 | $ref: "#/definitions/NewPet" 71 | responses: 72 | "200": 73 | description: "pet response" 74 | schema: 75 | $ref: "#/definitions/Pet" 76 | default: 77 | description: "unexpected error" 78 | schema: 79 | $ref: "#/definitions/ErrorModel" 80 | /pets/{id}: 81 | get: 82 | description: "Returns a user based on a single ID, if the user does not have access to the pet" 83 | operationId: "findPetById" 84 | produces: 85 | - "application/json" 86 | - "application/xml" 87 | - "text/xml" 88 | - "text/html" 89 | parameters: 90 | - 91 | name: "id" 92 | in: "path" 93 | description: "ID of pet to fetch" 94 | required: true 95 | type: "integer" 96 | format: "int64" 97 | responses: 98 | "200": 99 | description: "pet response" 100 | schema: 101 | $ref: "#/definitions/Pet" 102 | default: 103 | description: "unexpected error" 104 | schema: 105 | $ref: "#/definitions/ErrorModel" 106 | delete: 107 | description: "deletes a single pet based on the ID supplied" 108 | operationId: "deletePet" 109 | parameters: 110 | - 111 | name: "id" 112 | in: "path" 113 | description: "ID of pet to delete" 114 | required: true 115 | type: "integer" 116 | format: "int64" 117 | responses: 118 | "204": 119 | description: "pet deleted" 120 | default: 121 | description: "unexpected error" 122 | schema: 123 | $ref: "#/definitions/ErrorModel" 124 | definitions: 125 | Pet: 126 | type: "object" 127 | allOf: 128 | - 129 | $ref: "#/definitions/NewPet" 130 | - 131 | required: 132 | - "id" 133 | properties: 134 | id: 135 | type: "integer" 136 | format: "int64" 137 | NewPet: 138 | type: "object" 139 | required: 140 | - "name" 141 | properties: 142 | name: 143 | type: "string" 144 | tag: 145 | type: "string" 146 | ErrorModel: 147 | type: "object" 148 | required: 149 | - "code" 150 | - "message" 151 | properties: 152 | code: 153 | type: "integer" 154 | format: "int32" 155 | message: 156 | type: "string" 157 | 158 | -------------------------------------------------------------------------------- /src/openapi/data/v2/petstore_minimal.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | version: "1.0.0" 4 | title: "Swagger Petstore" 5 | description: "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification" 6 | termsOfService: "http://swagger.io/terms/" 7 | contact: 8 | name: "Swagger API Team" 9 | license: 10 | name: "MIT" 11 | host: "petstore.swagger.io" 12 | basePath: "/api" 13 | schemes: 14 | - "http" 15 | consumes: 16 | - "application/json" 17 | produces: 18 | - "application/json" 19 | paths: 20 | /pets: 21 | get: 22 | description: "Returns all pets from the system that the user has access to" 23 | produces: 24 | - "application/json" 25 | responses: 26 | "200": 27 | description: "A list of pets." 28 | schema: 29 | type: "array" 30 | items: 31 | $ref: "#/definitions/Pet" 32 | definitions: 33 | Pet: 34 | type: "object" 35 | required: 36 | - "id" 37 | - "name" 38 | properties: 39 | id: 40 | type: "integer" 41 | format: "int64" 42 | name: 43 | type: "string" 44 | tag: 45 | type: "string" -------------------------------------------------------------------------------- /src/openapi/data/v2/rocks.yaml: -------------------------------------------------------------------------------- 1 | swagger: "2.0" 2 | info: 3 | description: "My service rocks." 4 | version: "1.0.0" 5 | title: SOA Resource 6 | termsOfService: https://github.com/mdsol/resource_rocks 7 | schemes: 8 | - https 9 | consumes: 10 | - application/json 11 | produces: 12 | - application/json 13 | 14 | paths: 15 | /v1/resource: 16 | get: 17 | description: "A filtered collection of resource " 18 | operationId: getResource 19 | parameters: 20 | - $ref: "#/parameters/per_page" 21 | 22 | responses: 23 | "404": 24 | description: Resource not found 25 | 26 | parameters: 27 | per_page: 28 | in: query 29 | name: per_page 30 | description: The number of results per page 31 | required: false 32 | type: integer 33 | default: 25 34 | -------------------------------------------------------------------------------- /src/openapi/data/v2/uber.yaml: -------------------------------------------------------------------------------- 1 | # this is an example of the Uber API 2 | # as a demonstration of an API spec in YAML 3 | swagger: "2.0" 4 | info: 5 | title: Uber API 6 | description: Move your app forward with the Uber API 7 | version: "1.0.0" 8 | # the domain of the service 9 | host: api.uber.com 10 | # array of all schemes that your API supports 11 | schemes: 12 | - https 13 | # will be prefixed to all paths 14 | basePath: /v1 15 | securityDefinitions: 16 | apikey: 17 | type: apiKey 18 | name: server_token 19 | in: query 20 | produces: 21 | - application/json 22 | paths: 23 | /products: 24 | get: 25 | summary: Product Types 26 | description: The Products endpoint returns information about the Uber products offered at a given location. The response includes the display name and other details about each product, and lists the products in the proper display order. 27 | parameters: 28 | - name: latitude 29 | in: query 30 | description: Latitude component of location. 31 | required: true 32 | type: number 33 | format: double 34 | - name: longitude 35 | in: query 36 | description: Longitude component of location. 37 | required: true 38 | type: number 39 | format: double 40 | security: 41 | - apikey: [] 42 | tags: 43 | - Products 44 | responses: 45 | "200": 46 | description: An array of products 47 | schema: 48 | type: array 49 | items: 50 | $ref: '#/definitions/Product' 51 | default: 52 | description: Unexpected error 53 | schema: 54 | $ref: '#/definitions/Error' 55 | /estimates/price: 56 | get: 57 | summary: Price Estimates 58 | description: The Price Estimates endpoint returns an estimated price range for each product offered at a given location. The price estimate is provided as a formatted string with the full price range and the localized currency symbol.

The response also includes low and high estimates, and the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code for situations requiring currency conversion. When surge is active for a particular product, its surge_multiplier will be greater than 1, but the price estimate already factors in this multiplier. 59 | parameters: 60 | - name: start_latitude 61 | in: query 62 | description: Latitude component of start location. 63 | required: true 64 | type: number 65 | format: double 66 | - name: start_longitude 67 | in: query 68 | description: Longitude component of start location. 69 | required: true 70 | type: number 71 | format: double 72 | - name: end_latitude 73 | in: query 74 | description: Latitude component of end location. 75 | required: true 76 | type: number 77 | format: double 78 | - name: end_longitude 79 | in: query 80 | description: Longitude component of end location. 81 | required: true 82 | type: number 83 | format: double 84 | tags: 85 | - Estimates 86 | responses: 87 | "200": 88 | description: An array of price estimates by product 89 | schema: 90 | type: array 91 | items: 92 | $ref: '#/definitions/PriceEstimate' 93 | default: 94 | description: Unexpected error 95 | schema: 96 | $ref: '#/definitions/Error' 97 | /estimates/time: 98 | get: 99 | summary: Time Estimates 100 | description: The Time Estimates endpoint returns ETAs for all products offered at a given location, with the responses expressed as integers in seconds. We recommend that this endpoint be called every minute to provide the most accurate, up-to-date ETAs. 101 | parameters: 102 | - name: start_latitude 103 | in: query 104 | description: Latitude component of start location. 105 | required: true 106 | type: number 107 | format: double 108 | - name: start_longitude 109 | in: query 110 | description: Longitude component of start location. 111 | required: true 112 | type: number 113 | format: double 114 | - name: customer_uuid 115 | in: query 116 | type: string 117 | format: uuid 118 | description: Unique customer identifier to be used for experience customization. 119 | - name: product_id 120 | in: query 121 | type: string 122 | description: Unique identifier representing a specific product for a given latitude & longitude. 123 | tags: 124 | - Estimates 125 | responses: 126 | "200": 127 | description: An array of products 128 | schema: 129 | type: array 130 | items: 131 | $ref: '#/definitions/Product' 132 | default: 133 | description: Unexpected error 134 | schema: 135 | $ref: '#/definitions/Error' 136 | /me: 137 | get: 138 | summary: User Profile 139 | description: The User Profile endpoint returns information about the Uber user that has authorized with the application. 140 | tags: 141 | - User 142 | responses: 143 | "200": 144 | description: Profile information for a user 145 | schema: 146 | $ref: '#/definitions/Profile' 147 | default: 148 | description: Unexpected error 149 | schema: 150 | $ref: '#/definitions/Error' 151 | /history: 152 | get: 153 | summary: User Activity 154 | description: The User Activity endpoint returns data about a user's lifetime activity with Uber. The response will include pickup locations and times, dropoff locations and times, the distance of past requests, and information about which products were requested.

The history array in the response will have a maximum length based on the limit parameter. The response value count may exceed limit, therefore subsequent API requests may be necessary. 155 | parameters: 156 | - name: offset 157 | in: query 158 | type: integer 159 | format: int32 160 | description: Offset the list of returned results by this amount. Default is zero. 161 | - name: limit 162 | in: query 163 | type: integer 164 | format: int32 165 | description: Number of items to retrieve. Default is 5, maximum is 100. 166 | tags: 167 | - User 168 | responses: 169 | "200": 170 | description: History information for the given user 171 | schema: 172 | $ref: '#/definitions/Activities' 173 | default: 174 | description: Unexpected error 175 | schema: 176 | $ref: '#/definitions/Error' 177 | definitions: 178 | Product: 179 | properties: 180 | product_id: 181 | type: string 182 | description: Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles. 183 | description: 184 | type: string 185 | description: Description of product. 186 | display_name: 187 | type: string 188 | description: Display name of product. 189 | capacity: 190 | type: integer 191 | description: Capacity of product. For example, 4 people. 192 | image: 193 | type: string 194 | description: Image URL representing the product. 195 | ProductList: 196 | properties: 197 | products: 198 | description: Contains the list of products 199 | type: array 200 | items: 201 | $ref: "#/definitions/Product" 202 | PriceEstimate: 203 | properties: 204 | product_id: 205 | type: string 206 | description: Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles 207 | currency_code: 208 | type: string 209 | description: "[ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code." 210 | display_name: 211 | type: string 212 | description: Display name of product. 213 | estimate: 214 | type: string 215 | description: Formatted string of estimate in local currency of the start location. Estimate could be a range, a single number (flat rate) or "Metered" for TAXI. 216 | low_estimate: 217 | type: number 218 | description: Lower bound of the estimated price. 219 | high_estimate: 220 | type: number 221 | description: Upper bound of the estimated price. 222 | surge_multiplier: 223 | type: number 224 | description: Expected surge multiplier. Surge is active if surge_multiplier is greater than 1. Price estimate already factors in the surge multiplier. 225 | Profile: 226 | properties: 227 | first_name: 228 | type: string 229 | description: First name of the Uber user. 230 | last_name: 231 | type: string 232 | description: Last name of the Uber user. 233 | email: 234 | type: string 235 | description: Email address of the Uber user 236 | picture: 237 | type: string 238 | description: Image URL of the Uber user. 239 | promo_code: 240 | type: string 241 | description: Promo code of the Uber user. 242 | Activity: 243 | properties: 244 | uuid: 245 | type: string 246 | description: Unique identifier for the activity 247 | Activities: 248 | properties: 249 | offset: 250 | type: integer 251 | format: int32 252 | description: Position in pagination. 253 | limit: 254 | type: integer 255 | format: int32 256 | description: Number of items to retrieve (100 max). 257 | count: 258 | type: integer 259 | format: int32 260 | description: Total number of items available. 261 | history: 262 | type: array 263 | items: 264 | $ref: '#/definitions/Activity' 265 | Error: 266 | properties: 267 | code: 268 | type: integer 269 | format: int32 270 | message: 271 | type: string 272 | fields: 273 | type: string 274 | -------------------------------------------------------------------------------- /src/openapi/data/v3.0/api-with-examples.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | title: Simple API overview 4 | version: v2 5 | paths: 6 | /: 7 | get: 8 | operationId: listVersionsv2 9 | summary: List API versions 10 | responses: 11 | '200': 12 | description: |- 13 | 200 response 14 | content: 15 | application/json: 16 | examples: 17 | foo: 18 | value: { 19 | "versions": [ 20 | { 21 | "status": "CURRENT", 22 | "updated": "2011-01-21T11:33:21Z", 23 | "id": "v2.0", 24 | "links": [ 25 | { 26 | "href": "http://127.0.0.1:8774/v2/", 27 | "rel": "self" 28 | } 29 | ] 30 | }, 31 | { 32 | "status": "EXPERIMENTAL", 33 | "updated": "2013-07-23T11:33:21Z", 34 | "id": "v3.0", 35 | "links": [ 36 | { 37 | "href": "http://127.0.0.1:8774/v3/", 38 | "rel": "self" 39 | } 40 | ] 41 | } 42 | ] 43 | } 44 | '300': 45 | description: |- 46 | 300 response 47 | content: 48 | application/json: 49 | examples: 50 | foo: 51 | value: | 52 | { 53 | "versions": [ 54 | { 55 | "status": "CURRENT", 56 | "updated": "2011-01-21T11:33:21Z", 57 | "id": "v2.0", 58 | "links": [ 59 | { 60 | "href": "http://127.0.0.1:8774/v2/", 61 | "rel": "self" 62 | } 63 | ] 64 | }, 65 | { 66 | "status": "EXPERIMENTAL", 67 | "updated": "2013-07-23T11:33:21Z", 68 | "id": "v3.0", 69 | "links": [ 70 | { 71 | "href": "http://127.0.0.1:8774/v3/", 72 | "rel": "self" 73 | } 74 | ] 75 | } 76 | ] 77 | } 78 | /v2: 79 | get: 80 | operationId: getVersionDetailsv2 81 | summary: Show API version details 82 | responses: 83 | '200': 84 | description: |- 85 | 200 response 86 | content: 87 | application/json: 88 | examples: 89 | foo: 90 | value: { 91 | "version": { 92 | "status": "CURRENT", 93 | "updated": "2011-01-21T11:33:21Z", 94 | "media-types": [ 95 | { 96 | "base": "application/xml", 97 | "type": "application/vnd.openstack.compute+xml;version=2" 98 | }, 99 | { 100 | "base": "application/json", 101 | "type": "application/vnd.openstack.compute+json;version=2" 102 | } 103 | ], 104 | "id": "v2.0", 105 | "links": [ 106 | { 107 | "href": "http://127.0.0.1:8774/v2/", 108 | "rel": "self" 109 | }, 110 | { 111 | "href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf", 112 | "type": "application/pdf", 113 | "rel": "describedby" 114 | }, 115 | { 116 | "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", 117 | "type": "application/vnd.sun.wadl+xml", 118 | "rel": "describedby" 119 | }, 120 | { 121 | "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", 122 | "type": "application/vnd.sun.wadl+xml", 123 | "rel": "describedby" 124 | } 125 | ] 126 | } 127 | } 128 | '203': 129 | description: |- 130 | 203 response 131 | content: 132 | application/json: 133 | examples: 134 | foo: 135 | value: { 136 | "version": { 137 | "status": "CURRENT", 138 | "updated": "2011-01-21T11:33:21Z", 139 | "media-types": [ 140 | { 141 | "base": "application/xml", 142 | "type": "application/vnd.openstack.compute+xml;version=2" 143 | }, 144 | { 145 | "base": "application/json", 146 | "type": "application/vnd.openstack.compute+json;version=2" 147 | } 148 | ], 149 | "id": "v2.0", 150 | "links": [ 151 | { 152 | "href": "http://23.253.228.211:8774/v2/", 153 | "rel": "self" 154 | }, 155 | { 156 | "href": "http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf", 157 | "type": "application/pdf", 158 | "rel": "describedby" 159 | }, 160 | { 161 | "href": "http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl", 162 | "type": "application/vnd.sun.wadl+xml", 163 | "rel": "describedby" 164 | } 165 | ] 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/openapi/data/v3.0/callback-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: Callback Example 4 | version: 1.0.0 5 | paths: 6 | /streams: 7 | post: 8 | description: subscribes a client to receive out-of-band data 9 | parameters: 10 | - name: callbackUrl 11 | in: query 12 | required: true 13 | description: | 14 | the location where data will be sent. Must be network accessible 15 | by the source server 16 | schema: 17 | type: string 18 | format: uri 19 | example: https://tonys-server.com 20 | responses: 21 | '201': 22 | description: subscription successfully created 23 | content: 24 | application/json: 25 | schema: 26 | description: subscription information 27 | required: 28 | - subscriptionId 29 | properties: 30 | subscriptionId: 31 | description: this unique identifier allows management of the subscription 32 | type: string 33 | example: 2531329f-fb09-4ef7-887e-84e648214436 34 | callbacks: 35 | # the name `onData` is a convenience locator 36 | onData: 37 | # when data is sent, it will be sent to the `callbackUrl` provided 38 | # when making the subscription PLUS the suffix `/data` 39 | '{$request.query.callbackUrl}/data': 40 | post: 41 | requestBody: 42 | description: subscription payload 43 | content: 44 | application/json: 45 | schema: 46 | properties: 47 | timestamp: 48 | type: string 49 | format: date-time 50 | userData: 51 | type: string 52 | responses: 53 | '202': 54 | description: | 55 | Your server implementation should return this HTTP status code 56 | if the data was received successfully 57 | '204': 58 | description: | 59 | Your server should return this HTTP status code if no longer interested 60 | in further updates 61 | -------------------------------------------------------------------------------- /src/openapi/data/v3.0/link-example.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | info: 3 | title: Link Example 4 | version: 1.0.0 5 | paths: 6 | /2.0/users/{username}: 7 | get: 8 | operationId: getUserByName 9 | parameters: 10 | - name: username 11 | in: path 12 | required: true 13 | schema: 14 | type: string 15 | responses: 16 | '200': 17 | description: The User 18 | content: 19 | application/json: 20 | schema: 21 | $ref: '#/components/schemas/user' 22 | links: 23 | userRepositories: 24 | $ref: '#/components/links/UserRepositories' 25 | /2.0/repositories/{username}: 26 | get: 27 | operationId: getRepositoriesByOwner 28 | parameters: 29 | - name: username 30 | in: path 31 | required: true 32 | schema: 33 | type: string 34 | responses: 35 | '200': 36 | description: repositories owned by the supplied user 37 | content: 38 | application/json: 39 | schema: 40 | type: array 41 | items: 42 | $ref: '#/components/schemas/repository' 43 | links: 44 | userRepository: 45 | $ref: '#/components/links/UserRepository' 46 | /2.0/repositories/{username}/{slug}: 47 | get: 48 | operationId: getRepository 49 | parameters: 50 | - name: username 51 | in: path 52 | required: true 53 | schema: 54 | type: string 55 | - name: slug 56 | in: path 57 | required: true 58 | schema: 59 | type: string 60 | responses: 61 | '200': 62 | description: The repository 63 | content: 64 | application/json: 65 | schema: 66 | $ref: '#/components/schemas/repository' 67 | links: 68 | repositoryPullRequests: 69 | $ref: '#/components/links/RepositoryPullRequests' 70 | /2.0/repositories/{username}/{slug}/pullrequests: 71 | get: 72 | operationId: getPullRequestsByRepository 73 | parameters: 74 | - name: username 75 | in: path 76 | required: true 77 | schema: 78 | type: string 79 | - name: slug 80 | in: path 81 | required: true 82 | schema: 83 | type: string 84 | - name: state 85 | in: query 86 | schema: 87 | type: string 88 | enum: 89 | - open 90 | - merged 91 | - declined 92 | responses: 93 | '200': 94 | description: an array of pull request objects 95 | content: 96 | application/json: 97 | schema: 98 | type: array 99 | items: 100 | $ref: '#/components/schemas/pullrequest' 101 | /2.0/repositories/{username}/{slug}/pullrequests/{pid}: 102 | get: 103 | operationId: getPullRequestsById 104 | parameters: 105 | - name: username 106 | in: path 107 | required: true 108 | schema: 109 | type: string 110 | - name: slug 111 | in: path 112 | required: true 113 | schema: 114 | type: string 115 | - name: pid 116 | in: path 117 | required: true 118 | schema: 119 | type: string 120 | responses: 121 | '200': 122 | description: a pull request object 123 | content: 124 | application/json: 125 | schema: 126 | $ref: '#/components/schemas/pullrequest' 127 | links: 128 | pullRequestMerge: 129 | $ref: '#/components/links/PullRequestMerge' 130 | /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge: 131 | post: 132 | operationId: mergePullRequest 133 | parameters: 134 | - name: username 135 | in: path 136 | required: true 137 | schema: 138 | type: string 139 | - name: slug 140 | in: path 141 | required: true 142 | schema: 143 | type: string 144 | - name: pid 145 | in: path 146 | required: true 147 | schema: 148 | type: string 149 | responses: 150 | '204': 151 | description: the PR was successfully merged 152 | components: 153 | links: 154 | UserRepositories: 155 | # returns array of '#/components/schemas/repository' 156 | operationId: getRepositoriesByOwner 157 | parameters: 158 | username: $response.body#/username 159 | UserRepository: 160 | # returns '#/components/schemas/repository' 161 | operationId: getRepository 162 | parameters: 163 | username: $response.body#/owner/username 164 | slug: $response.body#/slug 165 | RepositoryPullRequests: 166 | # returns '#/components/schemas/pullrequest' 167 | operationId: getPullRequestsByRepository 168 | parameters: 169 | username: $response.body#/owner/username 170 | slug: $response.body#/slug 171 | PullRequestMerge: 172 | # executes /2.0/repositories/{username}/{slug}/pullrequests/{pid}/merge 173 | operationId: mergePullRequest 174 | parameters: 175 | username: $response.body#/author/username 176 | slug: $response.body#/repository/slug 177 | pid: $response.body#/id 178 | schemas: 179 | user: 180 | type: object 181 | properties: 182 | username: 183 | type: string 184 | uuid: 185 | type: string 186 | repository: 187 | type: object 188 | properties: 189 | slug: 190 | type: string 191 | owner: 192 | $ref: '#/components/schemas/user' 193 | pullrequest: 194 | type: object 195 | properties: 196 | id: 197 | type: integer 198 | title: 199 | type: string 200 | repository: 201 | $ref: '#/components/schemas/repository' 202 | author: 203 | $ref: '#/components/schemas/user' 204 | -------------------------------------------------------------------------------- /src/openapi/data/v3.0/petstore-expanded.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification 6 | termsOfService: http://swagger.io/terms/ 7 | contact: 8 | name: Swagger API Team 9 | email: foo@example.com 10 | url: http://madskristensen.net/ 11 | license: 12 | name: MIT 13 | url: http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT 14 | servers: 15 | - url: http://petstore.swagger.io/api 16 | paths: 17 | /pets: 18 | get: 19 | description: | 20 | Returns all pets from the system that the user has access to 21 | Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. 22 | 23 | Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. 24 | operationId: findPets 25 | parameters: 26 | - name: tags 27 | in: query 28 | description: tags to filter by 29 | required: false 30 | style: form 31 | schema: 32 | type: array 33 | items: 34 | type: string 35 | - name: limit 36 | in: query 37 | description: maximum number of results to return 38 | required: false 39 | schema: 40 | type: integer 41 | format: int32 42 | responses: 43 | '200': 44 | description: pet response 45 | content: 46 | application/json: 47 | schema: 48 | type: array 49 | items: 50 | $ref: '#/components/schemas/Pet' 51 | default: 52 | description: unexpected error 53 | content: 54 | application/json: 55 | schema: 56 | $ref: '#/components/schemas/Error' 57 | post: 58 | description: Creates a new pet in the store. Duplicates are allowed 59 | operationId: addPet 60 | requestBody: 61 | description: Pet to add to the store 62 | required: true 63 | content: 64 | application/json: 65 | schema: 66 | $ref: '#/components/schemas/NewPet' 67 | responses: 68 | '200': 69 | description: pet response 70 | content: 71 | application/json: 72 | schema: 73 | $ref: '#/components/schemas/Pet' 74 | default: 75 | description: unexpected error 76 | content: 77 | application/json: 78 | schema: 79 | $ref: '#/components/schemas/Error' 80 | /pets/{id}: 81 | get: 82 | description: Returns a user based on a single ID, if the user does not have access to the pet 83 | operationId: find pet by id 84 | parameters: 85 | - name: id 86 | in: path 87 | description: ID of pet to fetch 88 | required: true 89 | schema: 90 | type: integer 91 | format: int64 92 | responses: 93 | '200': 94 | description: pet response 95 | content: 96 | application/json: 97 | schema: 98 | $ref: '#/components/schemas/Pet' 99 | default: 100 | description: unexpected error 101 | content: 102 | application/json: 103 | schema: 104 | $ref: '#/components/schemas/Error' 105 | delete: 106 | description: deletes a single pet based on the ID supplied 107 | operationId: deletePet 108 | parameters: 109 | - name: id 110 | in: path 111 | description: ID of pet to delete 112 | required: true 113 | schema: 114 | type: integer 115 | format: int64 116 | responses: 117 | '204': 118 | description: pet deleted 119 | default: 120 | description: unexpected error 121 | content: 122 | application/json: 123 | schema: 124 | $ref: '#/components/schemas/Error' 125 | components: 126 | schemas: 127 | Pet: 128 | allOf: 129 | - $ref: '#/components/schemas/NewPet' 130 | - required: 131 | - id 132 | properties: 133 | id: 134 | type: integer 135 | format: int64 136 | 137 | NewPet: 138 | required: 139 | - name 140 | properties: 141 | name: 142 | type: string 143 | tag: 144 | type: string 145 | 146 | Error: 147 | required: 148 | - code 149 | - message 150 | properties: 151 | code: 152 | type: integer 153 | format: int32 154 | message: 155 | type: string 156 | 157 | PetSpecies: 158 | oneOf: 159 | - $ref: '#/components/schemas/Dog' 160 | - $ref: '#/components/schemas/Cat' 161 | 162 | Dog: 163 | properties: 164 | name: 165 | type: string 166 | age: 167 | type: int 168 | 169 | Cat: 170 | properties: 171 | name: 172 | type: string 173 | age: 174 | type: int 175 | lives: 176 | type: int 177 | -------------------------------------------------------------------------------- /src/openapi/data/v3.0/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pets: 11 | get: 12 | summary: List all pets 13 | operationId: listPets 14 | tags: 15 | - pets 16 | parameters: 17 | - name: limit 18 | in: query 19 | description: How many items to return at one time (max 100) 20 | required: false 21 | schema: 22 | type: integer 23 | format: int32 24 | responses: 25 | '200': 26 | description: An paged array of pets 27 | headers: 28 | x-next: 29 | description: A link to the next page of responses 30 | schema: 31 | type: string 32 | content: 33 | application/json: 34 | schema: 35 | $ref: "#/components/schemas/Pets" 36 | default: 37 | description: unexpected error 38 | content: 39 | application/json: 40 | schema: 41 | $ref: "#/components/schemas/Error" 42 | post: 43 | summary: Create a pet 44 | operationId: createPets 45 | tags: 46 | - pets 47 | responses: 48 | '201': 49 | description: Null response 50 | default: 51 | description: unexpected error 52 | content: 53 | application/json: 54 | schema: 55 | $ref: "#/components/schemas/Error" 56 | /pets/{petId}: 57 | get: 58 | summary: Info for a specific pet 59 | operationId: showPetById 60 | tags: 61 | - pets 62 | parameters: 63 | - name: petId 64 | in: path 65 | required: true 66 | description: The id of the pet to retrieve 67 | schema: 68 | type: string 69 | responses: 70 | '200': 71 | description: Expected response to a valid request 72 | content: 73 | application/json: 74 | schema: 75 | $ref: "#/components/schemas/Pets" 76 | default: 77 | description: unexpected error 78 | content: 79 | application/json: 80 | schema: 81 | $ref: "#/components/schemas/Error" 82 | components: 83 | schemas: 84 | Pet: 85 | required: 86 | - id 87 | - name 88 | properties: 89 | id: 90 | type: integer 91 | format: int64 92 | name: 93 | type: string 94 | tag: 95 | type: string 96 | Pets: 97 | type: array 98 | items: 99 | $ref: "#/components/schemas/Pet" 100 | Error: 101 | required: 102 | - code 103 | - message 104 | properties: 105 | code: 106 | type: integer 107 | format: int32 108 | message: 109 | type: string 110 | -------------------------------------------------------------------------------- /src/openapi/data/v3.0/uspto.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.0 2 | servers: 3 | - url: '{scheme}://developer.uspto.gov/ds-api' 4 | variables: 5 | scheme: 6 | description: 'The Data Set API is accessible via https and http' 7 | enum: 8 | - 'https' 9 | - 'http' 10 | default: 'https' 11 | info: 12 | description: >- 13 | The Data Set API (DSAPI) allows the public users to discover and search 14 | USPTO exported data sets. This is a generic API that allows USPTO users to 15 | make any CSV based data files searchable through API. With the help of GET 16 | call, it returns the list of data fields that are searchable. With the help 17 | of POST call, data can be fetched based on the filters on the field names. 18 | Please note that POST call is used to search the actual data. The reason for 19 | the POST call is that it allows users to specify any complex search criteria 20 | without worry about the GET size limitations as well as encoding of the 21 | input parameters. 22 | version: 1.0.0 23 | title: USPTO Data Set API 24 | contact: 25 | name: Open Data Portal 26 | url: 'https://developer.uspto.gov/' 27 | email: developer@uspto.gov 28 | tags: 29 | - name: metadata 30 | description: Find out about the data sets 31 | - name: search 32 | description: Search a data set 33 | paths: 34 | /: 35 | get: 36 | tags: 37 | - metadata 38 | operationId: list-data-sets 39 | summary: List available data sets 40 | responses: 41 | '200': 42 | description: Returns a list of data sets 43 | content: 44 | application/json: 45 | schema: 46 | $ref: '#/components/schemas/dataSetList' 47 | example: 48 | { 49 | "total": 2, 50 | "apis": [ 51 | { 52 | "apiKey": "oa_citations", 53 | "apiVersionNumber": "v1", 54 | "apiUrl": "https://developer.uspto.gov/ds-api/oa_citations/v1/fields", 55 | "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/oa_citations.json" 56 | }, 57 | { 58 | "apiKey": "cancer_moonshot", 59 | "apiVersionNumber": "v1", 60 | "apiUrl": "https://developer.uspto.gov/ds-api/cancer_moonshot/v1/fields", 61 | "apiDocumentationUrl": "https://developer.uspto.gov/ds-api-docs/index.html?url=https://developer.uspto.gov/ds-api/swagger/docs/cancer_moonshot.json" 62 | } 63 | ] 64 | } 65 | /{dataset}/{version}/fields: 66 | get: 67 | tags: 68 | - metadata 69 | summary: >- 70 | Provides the general information about the API and the list of fields 71 | that can be used to query the dataset. 72 | description: >- 73 | This GET API returns the list of all the searchable field names that are 74 | in the oa_citations. Please see the 'fields' attribute which returns an 75 | array of field names. Each field or a combination of fields can be 76 | searched using the syntax options shown below. 77 | operationId: list-searchable-fields 78 | parameters: 79 | - name: dataset 80 | in: path 81 | description: 'Name of the dataset. In this case, the default value is oa_citations' 82 | required: true 83 | schema: 84 | type: string 85 | default: oa_citations 86 | - name: version 87 | in: path 88 | description: Version of the dataset. 89 | required: true 90 | schema: 91 | type: string 92 | default: v1 93 | responses: 94 | '200': 95 | description: >- 96 | The dataset api for the given version is found and it is accessible 97 | to consume. 98 | content: 99 | application/json: 100 | schema: 101 | type: string 102 | '404': 103 | description: >- 104 | The combination of dataset name and version is not found in the 105 | system or it is not published yet to be consumed by public. 106 | content: 107 | application/json: 108 | schema: 109 | type: string 110 | /{dataset}/{version}/records: 111 | post: 112 | tags: 113 | - search 114 | summary: >- 115 | Provides search capability for the data set with the given search 116 | criteria. 117 | description: >- 118 | This API is based on Solr/Lucense Search. The data is indexed using 119 | SOLR. This GET API returns the list of all the searchable field names 120 | that are in the Solr Index. Please see the 'fields' attribute which 121 | returns an array of field names. Each field or a combination of fields 122 | can be searched using the Solr/Lucene Syntax. Please refer 123 | https://lucene.apache.org/core/3_6_2/queryparsersyntax.html#Overview for 124 | the query syntax. List of field names that are searchable can be 125 | determined using above GET api. 126 | operationId: perform-search 127 | parameters: 128 | - name: version 129 | in: path 130 | description: Version of the dataset. 131 | required: true 132 | schema: 133 | type: string 134 | default: v1 135 | - name: dataset 136 | in: path 137 | description: 'Name of the dataset. In this case, the default value is oa_citations' 138 | required: true 139 | schema: 140 | type: string 141 | default: oa_citations 142 | responses: 143 | '200': 144 | description: successful operation 145 | content: 146 | application/json: 147 | schema: 148 | type: array 149 | items: 150 | type: object 151 | additionalProperties: 152 | type: object 153 | '404': 154 | description: No matching record found for the given criteria. 155 | requestBody: 156 | content: 157 | application/x-www-form-urlencoded: 158 | schema: 159 | type: object 160 | properties: 161 | criteria: 162 | description: >- 163 | Uses Lucene Query Syntax in the format of 164 | propertyName:value, propertyName:[num1 TO num2] and date 165 | range format: propertyName:[yyyyMMdd TO yyyyMMdd]. In the 166 | response please see the 'docs' element which has the list of 167 | record objects. Each record structure would consist of all 168 | the fields and their corresponding values. 169 | type: string 170 | default: '*:*' 171 | start: 172 | description: Starting record number. Default value is 0. 173 | type: integer 174 | default: 0 175 | rows: 176 | description: >- 177 | Specify number of rows to be returned. If you run the search 178 | with default values, in the response you will see 'numFound' 179 | attribute which will tell the number of records available in 180 | the dataset. 181 | type: integer 182 | default: 100 183 | required: 184 | - criteria 185 | components: 186 | schemas: 187 | dataSetList: 188 | type: object 189 | properties: 190 | total: 191 | type: integer 192 | apis: 193 | type: array 194 | items: 195 | type: object 196 | properties: 197 | apiKey: 198 | type: string 199 | description: To be used as a dataset parameter value 200 | apiVersionNumber: 201 | type: string 202 | description: To be used as a version parameter value 203 | apiUrl: 204 | type: string 205 | format: uriref 206 | description: "The URL describing the dataset's fields" 207 | apiDocumentationUrl: 208 | type: string 209 | format: uriref 210 | description: A URL to the API console for each API 211 | -------------------------------------------------------------------------------- /src/openapi/error.rs: -------------------------------------------------------------------------------- 1 | //! Error types 2 | 3 | use semver::{Error as SemVerError, Version}; 4 | use serde_json::Error as JsonError; 5 | #[cfg(not(target_arch = "wasm32"))] 6 | use serde_yaml::Error as YamlError; 7 | use std::io::Error as IoError; 8 | use thiserror::Error; 9 | 10 | /// errors that openapi functions may return 11 | #[cfg(not(target_arch = "wasm32"))] 12 | #[derive(Error, Debug)] 13 | pub enum Error { 14 | #[error("{0}")] 15 | Io(IoError), 16 | #[error("{0}")] 17 | Yaml(YamlError), 18 | #[error("{0}")] 19 | Serialize(JsonError), 20 | #[error("{0}")] 21 | SemVerError(SemVerError), 22 | #[error("Unsupported spec file version ({0})")] 23 | UnsupportedSpecFileVersion(Version), 24 | } 25 | 26 | #[cfg(target_arch = "wasm32")] 27 | #[derive(Error, Debug)] 28 | pub enum Error { 29 | #[error("{0}")] 30 | Io(IoError), 31 | #[error("{0}")] 32 | Serialize(JsonError), 33 | #[error("{0}")] 34 | SemVerError(SemVerError), 35 | #[error("Unsupported spec file version ({0})")] 36 | UnsupportedSpecFileVersion(Version), 37 | } 38 | 39 | impl From for Error { 40 | fn from(e: IoError) -> Self { 41 | Error::Io(e) 42 | } 43 | } 44 | 45 | #[cfg(not(target_arch = "wasm32"))] 46 | impl From for Error { 47 | fn from(e: YamlError) -> Self { 48 | Error::Yaml(e) 49 | } 50 | } 51 | 52 | impl From for Error { 53 | fn from(e: JsonError) -> Self { 54 | Error::Serialize(e) 55 | } 56 | } 57 | 58 | impl From for Error { 59 | fn from(e: SemVerError) -> Self { 60 | Error::SemVerError(e) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/openapi/mod.rs: -------------------------------------------------------------------------------- 1 | //! Openapi provides structures and support for serializing and deserializing [openapi](https://github.com/OAI/OpenAPI-Specification) specifications 2 | //! 3 | //! 4 | //! # Errors 5 | //! 6 | //! Operations typically result in a `openapi::Result` Type which is an alias 7 | //! for Rust's 8 | //! built-in Result with the Err Type fixed to the 9 | //! [openapi::errors::Error](errors/struct.Error.html) enum type. These are 10 | //! provided 11 | //! using [error_chain](https://github.com/brson/error-chain) crate so their 12 | //! shape and behavior should be consistent and familiar to existing 13 | //! error_chain users. 14 | //! 15 | use std::result::Result as StdResult; 16 | #[cfg(not(target_arch = "wasm32"))] 17 | use std::{fs::File, io::Read, path::Path}; 18 | 19 | pub mod error; 20 | pub mod v3_0; 21 | 22 | pub use error::Error; 23 | 24 | const MINIMUM_OPENAPI30_VERSION: &str = ">= 3.0"; 25 | 26 | pub type Result = StdResult; 27 | 28 | /// Supported versions of the OpenApi. 29 | #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] 30 | #[serde(untagged)] 31 | pub enum OpenApi { 32 | /// Version 3.0.1 of the OpenApi specification. 33 | /// 34 | /// Refer to the official 35 | /// [specification](https://github.com/OAI/OpenAPI-Specification/blob/0dd79f6/versions/3.0.1.md) 36 | /// for more information. 37 | #[allow(non_camel_case_types)] 38 | V3_0(Box), 39 | } 40 | 41 | /// deserialize an open api spec from a path 42 | #[cfg(not(target_arch = "wasm32"))] 43 | pub fn from_path

(path: P) -> Result 44 | where 45 | P: AsRef, 46 | { 47 | from_reader(File::open(path)?) 48 | } 49 | 50 | /// deserialize an open api spec from type which implements Read 51 | #[cfg(not(target_arch = "wasm32"))] 52 | pub fn from_reader(read: R) -> Result 53 | where 54 | R: Read, 55 | { 56 | Ok(serde_yaml::from_reader::(read)?) 57 | } 58 | 59 | /// serialize to a yaml string 60 | #[cfg(not(target_arch = "wasm32"))] 61 | pub fn to_yaml(spec: &OpenApi) -> Result { 62 | Ok(serde_yaml::to_string(spec)?) 63 | } 64 | 65 | /// serialize to a json string 66 | pub fn to_json(spec: &OpenApi) -> Result { 67 | Ok(serde_json::to_string_pretty(spec)?) 68 | } 69 | 70 | #[cfg(not(target_arch = "wasm32"))] 71 | #[cfg(test)] 72 | mod tests { 73 | use super::*; 74 | use std::{ 75 | fs::{self, read_to_string, File}, 76 | io::Write, 77 | }; 78 | 79 | /// Helper function to write string to file. 80 | fn write_to_file

(path: P, filename: &str, data: &str) 81 | where 82 | P: AsRef + std::fmt::Debug, 83 | { 84 | println!(" Saving string to {:?}...", path); 85 | std::fs::create_dir_all(&path).unwrap(); 86 | let full_filename = path.as_ref().to_path_buf().join(filename); 87 | let mut f = File::create(&full_filename).unwrap(); 88 | f.write_all(data.as_bytes()).unwrap(); 89 | } 90 | 91 | /// Convert a YAML `&str` to a JSON `String`. 92 | fn convert_yaml_str_to_json(yaml_str: &str) -> String { 93 | let yaml: serde_yaml::Value = serde_yaml::from_str(yaml_str).unwrap(); 94 | let json: serde_json::Value = serde_yaml::from_value(yaml).unwrap(); 95 | serde_json::to_string_pretty(&json).unwrap() 96 | } 97 | 98 | /// Deserialize and re-serialize the input file to a JSON string through two different 99 | /// paths, comparing the result. 100 | /// 1. File -> `String` -> `serde_yaml::Value` -> `serde_json::Value` -> `String` 101 | /// 2. File -> `Spec` -> `serde_json::Value` -> `String` 102 | /// Both conversion of `serde_json::Value` -> `String` are done 103 | /// using `serde_json::to_string_pretty`. 104 | /// Since the first conversion is independant of the current crate (and only 105 | /// uses serde's json and yaml support), no information should be lost in the final 106 | /// JSON string. The second conversion goes through our `OpenApi`, so the final JSON 107 | /// string is a representation of _our_ implementation. 108 | /// By comparing those two JSON conversions, we can validate our implementation. 109 | fn compare_spec_through_json( 110 | input_file: &Path, 111 | save_path_base: &Path, 112 | ) -> (String, String, String) { 113 | // First conversion: 114 | // File -> `String` -> `serde_yaml::Value` -> `serde_json::Value` -> `String` 115 | 116 | // Read the original file to string 117 | let spec_yaml_str = read_to_string(&input_file) 118 | .unwrap_or_else(|e| panic!("failed to read contents of {:?}: {}", input_file, e)); 119 | // Convert YAML string to JSON string 120 | let spec_json_str = convert_yaml_str_to_json(&spec_yaml_str); 121 | 122 | // Second conversion: 123 | // File -> `Spec` -> `serde_json::Value` -> `String` 124 | 125 | // Parse the input file 126 | let parsed_spec = from_path(&input_file).unwrap(); 127 | // Convert to serde_json::Value 128 | let parsed_spec_json = serde_json::to_value(parsed_spec).unwrap(); 129 | // Convert to a JSON string 130 | let parsed_spec_json_str: String = serde_json::to_string_pretty(&parsed_spec_json).unwrap(); 131 | 132 | // Save JSON strings to file 133 | let api_filename = input_file 134 | .file_name() 135 | .unwrap() 136 | .to_str() 137 | .unwrap() 138 | .replace(".yaml", ".json"); 139 | 140 | let mut save_path = save_path_base.to_path_buf(); 141 | save_path.push("yaml_to_json"); 142 | write_to_file(&save_path, &api_filename, &spec_json_str); 143 | 144 | let mut save_path = save_path_base.to_path_buf(); 145 | save_path.push("yaml_to_spec_to_json"); 146 | write_to_file(&save_path, &api_filename, &parsed_spec_json_str); 147 | 148 | // Return the JSON filename and the two JSON strings 149 | (api_filename, parsed_spec_json_str, spec_json_str) 150 | } 151 | 152 | // Just tests if the deserialization does not blow up. But does not test correctness 153 | #[test] 154 | fn can_deserialize() { 155 | for entry in fs::read_dir("src/openapi/data/v3.0").unwrap() { 156 | let path = entry.unwrap().path(); 157 | // cargo test -- --nocapture to see this message 158 | println!("Testing if {:?} is deserializable", path); 159 | from_path(path).unwrap(); 160 | } 161 | } 162 | 163 | #[test] 164 | fn can_deserialize_and_reserialize_v3() { 165 | let save_path_base: std::path::PathBuf = 166 | ["target", "tests", "can_deserialize_and_reserialize_v3"] 167 | .iter() 168 | .collect(); 169 | 170 | for entry in fs::read_dir("src/openapi/data/v3.0").unwrap() { 171 | let entry = entry.unwrap(); 172 | let path = entry.path(); 173 | 174 | println!("Testing if {:?} is deserializable", path); 175 | 176 | let (api_filename, parsed_spec_json_str, spec_json_str) = 177 | compare_spec_through_json(&path, &save_path_base); 178 | 179 | assert_eq!( 180 | parsed_spec_json_str.lines().collect::>(), 181 | spec_json_str.lines().collect::>(), 182 | "contents did not match for api {}", 183 | api_filename 184 | ); 185 | } 186 | } 187 | 188 | #[test] 189 | fn can_deserialize_one_of_v3() { 190 | let openapi = from_path("src/openapi/data/v3.0/petstore-expanded.yaml").unwrap(); 191 | let OpenApi::V3_0(spec) = openapi; 192 | let components = spec.components.unwrap(); 193 | let schemas = components.schemas.unwrap(); 194 | let obj_or_ref = schemas.get("PetSpecies"); 195 | 196 | if let Some(v3_0::ObjectOrReference::Object(schema)) = obj_or_ref { 197 | // there should be 2 schemas in there 198 | assert_eq!(schema.one_of.as_ref().unwrap().len(), 2); 199 | } else { 200 | panic!("object should have been schema"); 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/openapi/v3_0/components.rs: -------------------------------------------------------------------------------- 1 | use super::schema::{ 2 | Callback, Example, Header, Link, Parameter, RequestBody, Response, Schema, SecurityScheme, 3 | }; 4 | use std::collections::BTreeMap; 5 | 6 | #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] 7 | #[serde(untagged)] 8 | pub enum ObjectOrReference { 9 | Object(T), 10 | Ref { 11 | #[serde(rename = "$ref")] 12 | ref_path: String, 13 | }, 14 | } 15 | 16 | /// Holds a set of reusable objects for different aspects of the OAS. 17 | /// 18 | /// All objects defined within the components object will have no effect on the API unless 19 | /// they are explicitly referenced from properties outside the components object. 20 | /// 21 | /// See . 22 | #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Default)] 23 | pub struct Components { 24 | /// An object to hold reusable Schema Objects. 25 | #[serde(skip_serializing_if = "Option::is_none")] 26 | pub schemas: Option>>, 27 | 28 | /// An object to hold reusable Response Objects. 29 | #[serde(skip_serializing_if = "Option::is_none")] 30 | pub responses: Option>>, 31 | 32 | /// An object to hold reusable Parameter Objects. 33 | #[serde(skip_serializing_if = "Option::is_none")] 34 | pub parameters: Option>>, 35 | 36 | /// An object to hold reusable Example 37 | #[serde(skip_serializing_if = "Option::is_none")] 38 | pub examples: Option>>, 39 | 40 | /// An object to hold reusable Request Body Objects. 41 | #[serde(skip_serializing_if = "Option::is_none", rename = "requestBodies")] 42 | pub request_bodies: Option>>, 43 | 44 | /// An object to hold reusable Header Objects. 45 | #[serde(skip_serializing_if = "Option::is_none")] 46 | pub headers: Option>>, 47 | 48 | /// An object to hold reusable Security Scheme Objects. 49 | #[serde(skip_serializing_if = "Option::is_none", rename = "securitySchemes")] 50 | pub security_schemes: Option>>, 51 | 52 | /// An object to hold reusable Link Objects. 53 | #[serde(skip_serializing_if = "Option::is_none")] 54 | pub links: Option>>, 55 | 56 | /// An object to hold reusable Callback Objects. 57 | #[serde(skip_serializing_if = "Option::is_none")] 58 | pub callbacks: Option>>, 59 | // TODO: Add "Specification Extensions" https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#specificationExtensions} 60 | } 61 | -------------------------------------------------------------------------------- /src/openapi/v3_0/mod.rs: -------------------------------------------------------------------------------- 1 | //! Support for OpenApi version 3.0.1 specification. 2 | //! 3 | //! See the 4 | //! [specification](https://github.com/OAI/OpenAPI-Specification/blob/0dd79f6/versions/3.0.1.md) 5 | //! for more information. 6 | 7 | mod components; 8 | mod schema; 9 | 10 | pub use self::{components::*, schema::*}; 11 | -------------------------------------------------------------------------------- /tests/fixtures/api-key.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "2a763711-fdbf-4c82-a7eb-f27c3db887e7", 4 | "name": "OAuth Example", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "updatedAt": "2023-11-22T19:23:11.000Z", 7 | "uid": "10354132-2a763711-fdbf-4c82-a7eb-f27c3db887e7" 8 | }, 9 | "item": [], 10 | "auth": { 11 | "type": "apikey", 12 | "apikey": [ 13 | { 14 | "key": "key", 15 | "value": "Authorization", 16 | "type": "string" 17 | } 18 | ] 19 | }, 20 | "event": [ 21 | { 22 | "listen": "prerequest", 23 | "script": { 24 | "id": "9c8dd9ec-1d7d-4f55-9f7c-142596e1fbe1", 25 | "type": "text/javascript", 26 | "exec": [""] 27 | } 28 | }, 29 | { 30 | "listen": "test", 31 | "script": { 32 | "id": "c98be8eb-6903-4762-a44b-3a9d0a6d8865", 33 | "type": "text/javascript", 34 | "exec": [""] 35 | } 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /tests/fixtures/calculator-soap.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "1fa4b1c7-84b4-44d9-b611-748d2d5a55ab", 4 | "name": "Calculator", 5 | "description": "\n", 6 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 7 | "_exporter_id": "10354132", 8 | "_collection_link": "https://postman.postman.co/workspace/Kevin's-Team-Workspace~5f614b35-0358-4fdd-85d9-72225ed9b063/collection/10354132-1fa4b1c7-84b4-44d9-b611-748d2d5a55ab?source=collection_link" 9 | }, 10 | "item": [ 11 | { 12 | "name": "CalculatorSoap", 13 | "item": [ 14 | { 15 | "name": "Add", 16 | "request": { 17 | "method": "POST", 18 | "header": [ 19 | { 20 | "key": "Content-Type", 21 | "value": "text/xml; charset=utf-8" 22 | }, 23 | { 24 | "key": "SOAPAction", 25 | "value": "http://tempuri.org/Add" 26 | } 27 | ], 28 | "body": { 29 | "mode": "raw", 30 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 31 | "options": { 32 | "raw": { 33 | "language": "xml" 34 | } 35 | } 36 | }, 37 | "url": { 38 | "raw": "{{CalculatorSoapBaseUrl}}/calculator.asmx", 39 | "host": [ 40 | "{{CalculatorSoapBaseUrl}}" 41 | ], 42 | "path": [ 43 | "calculator.asmx" 44 | ] 45 | }, 46 | "description": "Adds two integers. This is a test WebService. ©DNE Online" 47 | }, 48 | "response": [ 49 | { 50 | "name": "Add response", 51 | "originalRequest": { 52 | "method": "POST", 53 | "header": [ 54 | { 55 | "key": "Content-Type", 56 | "value": "text/xml; charset=utf-8" 57 | }, 58 | { 59 | "key": "SOAPAction", 60 | "value": "http://tempuri.org/Add" 61 | } 62 | ], 63 | "body": { 64 | "mode": "raw", 65 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 66 | "options": { 67 | "raw": { 68 | "language": "xml" 69 | } 70 | } 71 | }, 72 | "url": { 73 | "raw": "http://www.dneonline.com/calculator.asmx", 74 | "protocol": "http", 75 | "host": [ 76 | "www", 77 | "dneonline", 78 | "com" 79 | ], 80 | "path": [ 81 | "calculator.asmx" 82 | ] 83 | } 84 | }, 85 | "status": "OK", 86 | "code": 200, 87 | "_postman_previewlanguage": "xml", 88 | "header": [ 89 | { 90 | "key": "Content-Type", 91 | "value": "text/xml; charset=utf-8" 92 | } 93 | ], 94 | "cookie": [], 95 | "body": "\n\n \n \n 100\n \n \n\n" 96 | } 97 | ] 98 | }, 99 | { 100 | "name": "Subtract", 101 | "request": { 102 | "method": "POST", 103 | "header": [ 104 | { 105 | "key": "Content-Type", 106 | "value": "text/xml; charset=utf-8" 107 | }, 108 | { 109 | "key": "SOAPAction", 110 | "value": "http://tempuri.org/Subtract" 111 | } 112 | ], 113 | "body": { 114 | "mode": "raw", 115 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 116 | "options": { 117 | "raw": { 118 | "language": "xml" 119 | } 120 | } 121 | }, 122 | "url": { 123 | "raw": "{{CalculatorSoapBaseUrl}}/calculator.asmx", 124 | "host": [ 125 | "{{CalculatorSoapBaseUrl}}" 126 | ], 127 | "path": [ 128 | "calculator.asmx" 129 | ] 130 | } 131 | }, 132 | "response": [ 133 | { 134 | "name": "Subtract response", 135 | "originalRequest": { 136 | "method": "POST", 137 | "header": [ 138 | { 139 | "key": "Content-Type", 140 | "value": "text/xml; charset=utf-8" 141 | }, 142 | { 143 | "key": "SOAPAction", 144 | "value": "http://tempuri.org/Subtract" 145 | } 146 | ], 147 | "body": { 148 | "mode": "raw", 149 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 150 | "options": { 151 | "raw": { 152 | "language": "xml" 153 | } 154 | } 155 | }, 156 | "url": { 157 | "raw": "http://www.dneonline.com/calculator.asmx", 158 | "protocol": "http", 159 | "host": [ 160 | "www", 161 | "dneonline", 162 | "com" 163 | ], 164 | "path": [ 165 | "calculator.asmx" 166 | ] 167 | } 168 | }, 169 | "status": "OK", 170 | "code": 200, 171 | "_postman_previewlanguage": "xml", 172 | "header": [ 173 | { 174 | "key": "Content-Type", 175 | "value": "text/xml; charset=utf-8" 176 | } 177 | ], 178 | "cookie": [], 179 | "body": "\n\n \n \n 100\n \n \n\n" 180 | } 181 | ] 182 | }, 183 | { 184 | "name": "Multiply", 185 | "request": { 186 | "method": "POST", 187 | "header": [ 188 | { 189 | "key": "Content-Type", 190 | "value": "text/xml; charset=utf-8" 191 | }, 192 | { 193 | "key": "SOAPAction", 194 | "value": "http://tempuri.org/Multiply" 195 | } 196 | ], 197 | "body": { 198 | "mode": "raw", 199 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 200 | "options": { 201 | "raw": { 202 | "language": "xml" 203 | } 204 | } 205 | }, 206 | "url": { 207 | "raw": "{{CalculatorSoapBaseUrl}}/calculator.asmx", 208 | "host": [ 209 | "{{CalculatorSoapBaseUrl}}" 210 | ], 211 | "path": [ 212 | "calculator.asmx" 213 | ] 214 | } 215 | }, 216 | "response": [ 217 | { 218 | "name": "Multiply response", 219 | "originalRequest": { 220 | "method": "POST", 221 | "header": [ 222 | { 223 | "key": "Content-Type", 224 | "value": "text/xml; charset=utf-8" 225 | }, 226 | { 227 | "key": "SOAPAction", 228 | "value": "http://tempuri.org/Multiply" 229 | } 230 | ], 231 | "body": { 232 | "mode": "raw", 233 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 234 | "options": { 235 | "raw": { 236 | "language": "xml" 237 | } 238 | } 239 | }, 240 | "url": { 241 | "raw": "http://www.dneonline.com/calculator.asmx", 242 | "protocol": "http", 243 | "host": [ 244 | "www", 245 | "dneonline", 246 | "com" 247 | ], 248 | "path": [ 249 | "calculator.asmx" 250 | ] 251 | } 252 | }, 253 | "status": "OK", 254 | "code": 200, 255 | "_postman_previewlanguage": "xml", 256 | "header": [ 257 | { 258 | "key": "Content-Type", 259 | "value": "text/xml; charset=utf-8" 260 | } 261 | ], 262 | "cookie": [], 263 | "body": "\n\n \n \n 100\n \n \n\n" 264 | } 265 | ] 266 | }, 267 | { 268 | "name": "Divide", 269 | "request": { 270 | "method": "POST", 271 | "header": [ 272 | { 273 | "key": "Content-Type", 274 | "value": "text/xml; charset=utf-8" 275 | }, 276 | { 277 | "key": "SOAPAction", 278 | "value": "http://tempuri.org/Divide" 279 | } 280 | ], 281 | "body": { 282 | "mode": "raw", 283 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 284 | "options": { 285 | "raw": { 286 | "language": "xml" 287 | } 288 | } 289 | }, 290 | "url": { 291 | "raw": "{{CalculatorSoapBaseUrl}}/calculator.asmx", 292 | "host": [ 293 | "{{CalculatorSoapBaseUrl}}" 294 | ], 295 | "path": [ 296 | "calculator.asmx" 297 | ] 298 | } 299 | }, 300 | "response": [ 301 | { 302 | "name": "Divide response", 303 | "originalRequest": { 304 | "method": "POST", 305 | "header": [ 306 | { 307 | "key": "Content-Type", 308 | "value": "text/xml; charset=utf-8" 309 | }, 310 | { 311 | "key": "SOAPAction", 312 | "value": "http://tempuri.org/Divide" 313 | } 314 | ], 315 | "body": { 316 | "mode": "raw", 317 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 318 | "options": { 319 | "raw": { 320 | "language": "xml" 321 | } 322 | } 323 | }, 324 | "url": { 325 | "raw": "http://www.dneonline.com/calculator.asmx", 326 | "protocol": "http", 327 | "host": [ 328 | "www", 329 | "dneonline", 330 | "com" 331 | ], 332 | "path": [ 333 | "calculator.asmx" 334 | ] 335 | } 336 | }, 337 | "status": "OK", 338 | "code": 200, 339 | "_postman_previewlanguage": "xml", 340 | "header": [ 341 | { 342 | "key": "Content-Type", 343 | "value": "text/xml; charset=utf-8" 344 | } 345 | ], 346 | "cookie": [], 347 | "body": "\n\n \n \n 100\n \n \n\n" 348 | } 349 | ] 350 | } 351 | ] 352 | }, 353 | { 354 | "name": "CalculatorSoap12", 355 | "item": [ 356 | { 357 | "name": "Add", 358 | "request": { 359 | "method": "POST", 360 | "header": [ 361 | { 362 | "key": "Content-Type", 363 | "value": "text/xml; charset=utf-8" 364 | }, 365 | { 366 | "key": "SOAPAction", 367 | "value": "http://tempuri.org/Add" 368 | } 369 | ], 370 | "body": { 371 | "mode": "raw", 372 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 373 | "options": { 374 | "raw": { 375 | "language": "xml" 376 | } 377 | } 378 | }, 379 | "url": { 380 | "raw": "{{CalculatorSoap12BaseUrl}}/calculator.asmx", 381 | "host": [ 382 | "{{CalculatorSoap12BaseUrl}}" 383 | ], 384 | "path": [ 385 | "calculator.asmx" 386 | ] 387 | }, 388 | "description": "Adds two integers. This is a test WebService. ©DNE Online" 389 | }, 390 | "response": [ 391 | { 392 | "name": "Add response", 393 | "originalRequest": { 394 | "method": "POST", 395 | "header": [ 396 | { 397 | "key": "Content-Type", 398 | "value": "text/xml; charset=utf-8" 399 | }, 400 | { 401 | "key": "SOAPAction", 402 | "value": "http://tempuri.org/Add" 403 | } 404 | ], 405 | "body": { 406 | "mode": "raw", 407 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 408 | "options": { 409 | "raw": { 410 | "language": "xml" 411 | } 412 | } 413 | }, 414 | "url": { 415 | "raw": "http://www.dneonline.com/calculator.asmx", 416 | "protocol": "http", 417 | "host": [ 418 | "www", 419 | "dneonline", 420 | "com" 421 | ], 422 | "path": [ 423 | "calculator.asmx" 424 | ] 425 | } 426 | }, 427 | "status": "OK", 428 | "code": 200, 429 | "_postman_previewlanguage": "xml", 430 | "header": [ 431 | { 432 | "key": "Content-Type", 433 | "value": "text/xml; charset=utf-8" 434 | } 435 | ], 436 | "cookie": [], 437 | "body": "\n\n \n \n 100\n \n \n\n" 438 | } 439 | ] 440 | }, 441 | { 442 | "name": "Subtract", 443 | "request": { 444 | "method": "POST", 445 | "header": [ 446 | { 447 | "key": "Content-Type", 448 | "value": "text/xml; charset=utf-8" 449 | }, 450 | { 451 | "key": "SOAPAction", 452 | "value": "http://tempuri.org/Subtract" 453 | } 454 | ], 455 | "body": { 456 | "mode": "raw", 457 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 458 | "options": { 459 | "raw": { 460 | "language": "xml" 461 | } 462 | } 463 | }, 464 | "url": { 465 | "raw": "{{CalculatorSoap12BaseUrl}}/calculator.asmx", 466 | "host": [ 467 | "{{CalculatorSoap12BaseUrl}}" 468 | ], 469 | "path": [ 470 | "calculator.asmx" 471 | ] 472 | } 473 | }, 474 | "response": [ 475 | { 476 | "name": "Subtract response", 477 | "originalRequest": { 478 | "method": "POST", 479 | "header": [ 480 | { 481 | "key": "Content-Type", 482 | "value": "text/xml; charset=utf-8" 483 | }, 484 | { 485 | "key": "SOAPAction", 486 | "value": "http://tempuri.org/Subtract" 487 | } 488 | ], 489 | "body": { 490 | "mode": "raw", 491 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 492 | "options": { 493 | "raw": { 494 | "language": "xml" 495 | } 496 | } 497 | }, 498 | "url": { 499 | "raw": "http://www.dneonline.com/calculator.asmx", 500 | "protocol": "http", 501 | "host": [ 502 | "www", 503 | "dneonline", 504 | "com" 505 | ], 506 | "path": [ 507 | "calculator.asmx" 508 | ] 509 | } 510 | }, 511 | "status": "OK", 512 | "code": 200, 513 | "_postman_previewlanguage": "xml", 514 | "header": [ 515 | { 516 | "key": "Content-Type", 517 | "value": "text/xml; charset=utf-8" 518 | } 519 | ], 520 | "cookie": [], 521 | "body": "\n\n \n \n 100\n \n \n\n" 522 | } 523 | ] 524 | }, 525 | { 526 | "name": "Multiply", 527 | "request": { 528 | "method": "POST", 529 | "header": [ 530 | { 531 | "key": "Content-Type", 532 | "value": "text/xml; charset=utf-8" 533 | }, 534 | { 535 | "key": "SOAPAction", 536 | "value": "http://tempuri.org/Multiply" 537 | } 538 | ], 539 | "body": { 540 | "mode": "raw", 541 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 542 | "options": { 543 | "raw": { 544 | "language": "xml" 545 | } 546 | } 547 | }, 548 | "url": { 549 | "raw": "{{CalculatorSoap12BaseUrl}}/calculator.asmx", 550 | "host": [ 551 | "{{CalculatorSoap12BaseUrl}}" 552 | ], 553 | "path": [ 554 | "calculator.asmx" 555 | ] 556 | } 557 | }, 558 | "response": [ 559 | { 560 | "name": "Multiply response", 561 | "originalRequest": { 562 | "method": "POST", 563 | "header": [ 564 | { 565 | "key": "Content-Type", 566 | "value": "text/xml; charset=utf-8" 567 | }, 568 | { 569 | "key": "SOAPAction", 570 | "value": "http://tempuri.org/Multiply" 571 | } 572 | ], 573 | "body": { 574 | "mode": "raw", 575 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 576 | "options": { 577 | "raw": { 578 | "language": "xml" 579 | } 580 | } 581 | }, 582 | "url": { 583 | "raw": "http://www.dneonline.com/calculator.asmx", 584 | "protocol": "http", 585 | "host": [ 586 | "www", 587 | "dneonline", 588 | "com" 589 | ], 590 | "path": [ 591 | "calculator.asmx" 592 | ] 593 | } 594 | }, 595 | "status": "OK", 596 | "code": 200, 597 | "_postman_previewlanguage": "xml", 598 | "header": [ 599 | { 600 | "key": "Content-Type", 601 | "value": "text/xml; charset=utf-8" 602 | } 603 | ], 604 | "cookie": [], 605 | "body": "\n\n \n \n 100\n \n \n\n" 606 | } 607 | ] 608 | }, 609 | { 610 | "name": "Divide", 611 | "request": { 612 | "method": "POST", 613 | "header": [ 614 | { 615 | "key": "Content-Type", 616 | "value": "text/xml; charset=utf-8" 617 | }, 618 | { 619 | "key": "SOAPAction", 620 | "value": "http://tempuri.org/Divide" 621 | } 622 | ], 623 | "body": { 624 | "mode": "raw", 625 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 626 | "options": { 627 | "raw": { 628 | "language": "xml" 629 | } 630 | } 631 | }, 632 | "url": { 633 | "raw": "{{CalculatorSoap12BaseUrl}}/calculator.asmx", 634 | "host": [ 635 | "{{CalculatorSoap12BaseUrl}}" 636 | ], 637 | "path": [ 638 | "calculator.asmx" 639 | ] 640 | } 641 | }, 642 | "response": [ 643 | { 644 | "name": "Divide response", 645 | "originalRequest": { 646 | "method": "POST", 647 | "header": [ 648 | { 649 | "key": "Content-Type", 650 | "value": "text/xml; charset=utf-8" 651 | }, 652 | { 653 | "key": "SOAPAction", 654 | "value": "http://tempuri.org/Divide" 655 | } 656 | ], 657 | "body": { 658 | "mode": "raw", 659 | "raw": "\n\n \n \n 100\n 100\n \n \n\n", 660 | "options": { 661 | "raw": { 662 | "language": "xml" 663 | } 664 | } 665 | }, 666 | "url": { 667 | "raw": "http://www.dneonline.com/calculator.asmx", 668 | "protocol": "http", 669 | "host": [ 670 | "www", 671 | "dneonline", 672 | "com" 673 | ], 674 | "path": [ 675 | "calculator.asmx" 676 | ] 677 | } 678 | }, 679 | "status": "OK", 680 | "code": 200, 681 | "_postman_previewlanguage": "xml", 682 | "header": [ 683 | { 684 | "key": "Content-Type", 685 | "value": "text/xml; charset=utf-8" 686 | } 687 | ], 688 | "cookie": [], 689 | "body": "\n\n \n \n 100\n \n \n\n" 690 | } 691 | ] 692 | } 693 | ] 694 | } 695 | ], 696 | "variable": [ 697 | { 698 | "key": "CalculatorSoapBaseUrl", 699 | "value": "http://www.dneonline.com", 700 | "type": "any" 701 | }, 702 | { 703 | "key": "CalculatorSoap12BaseUrl", 704 | "value": "http://www.dneonline.com", 705 | "type": "any" 706 | } 707 | ] 708 | } -------------------------------------------------------------------------------- /tests/fixtures/empty-header-object.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "2a763711-fdbf-4c82-a7eb-f27c3db887e7", 4 | "name": "Empty Header Object example", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "updatedAt": "2023-11-22T17:23:18.000Z", 7 | "uid": "10354132-2a763711-fdbf-4c82-a7eb-f27c3db887e7" 8 | }, 9 | "item": [ 10 | { 11 | "name": "GET Request", 12 | "event": [ 13 | { 14 | "listen": "test", 15 | "script": { 16 | "id": "2298dced-107f-4b06-afe7-8f1835d4477f", 17 | "exec": [ 18 | "pm.test(\"response is ok\", function () {", 19 | " pm.response.to.have.status(200);", 20 | "});", 21 | "", 22 | "pm.test(\"response body has json with request queries\", function () {", 23 | " pm.response.to.have.jsonBody('args.foo1', 'bar1')", 24 | " .and.have.jsonBody('args.foo2', 'bar2');", 25 | "});" 26 | ], 27 | "type": "text/javascript" 28 | } 29 | } 30 | ], 31 | "request": { 32 | "method": "GET", 33 | "header": [], 34 | "url": { 35 | "raw": "https://postman-echo.com/get?foo1=bar1&foo2=bar2", 36 | "protocol": "https", 37 | "host": ["postman-echo", "com"], 38 | "path": ["get"], 39 | "query": [ 40 | { 41 | "key": "foo1", 42 | "value": "bar1" 43 | }, 44 | { 45 | "key": "foo2", 46 | "value": "bar2" 47 | } 48 | ] 49 | }, 50 | "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." 51 | }, 52 | "response": [ 53 | { 54 | "name": "GET Request Woops", 55 | "originalRequest": { 56 | "method": "GET", 57 | "header": [], 58 | "url": { 59 | "raw": "https://postman-echo.com/get?foo1=bar1&foo2=bar2", 60 | "protocol": "https", 61 | "host": ["postman-echo", "com"], 62 | "path": ["get"], 63 | "query": [ 64 | { 65 | "key": "foo1", 66 | "value": "bar1" 67 | }, 68 | { 69 | "key": "foo2", 70 | "value": "bar2" 71 | } 72 | ] 73 | } 74 | }, 75 | "status": "OK", 76 | "code": 200, 77 | "_postman_previewlanguage": "json", 78 | "header": [ 79 | {}, 80 | { 81 | "key": "Content-Type", 82 | "value": "application/json; charset=utf-8" 83 | }, 84 | { 85 | "key": "Date", 86 | "value": "Tue, 11 Jun 2019 10:43:13 GMT" 87 | }, 88 | { 89 | "key": "ETag", 90 | "value": "W/\"161-aLhNcsGArlgLSKbxPqfBW3viHPI\"" 91 | }, 92 | { 93 | "key": "Server", 94 | "value": "nginx" 95 | }, 96 | { 97 | "key": "set-cookie", 98 | "value": "sails.sid=s%3AGz-wblZgXE8FCDq7aJpx_tUgZUcG3Nsw.LdNEN8L0C7nGWkvGLwvdw6R2s6Syjr%2FzkvyevA8qR0c; Path=/; HttpOnly" 99 | }, 100 | { 101 | "key": "Vary", 102 | "value": "Accept-Encoding" 103 | }, 104 | { 105 | "key": "Content-Length", 106 | "value": "249" 107 | }, 108 | { 109 | "key": "Connection", 110 | "value": "keep-alive" 111 | } 112 | ], 113 | "cookie": [], 114 | "body": "{\n \"args\": {\n \"foo1\": \"bar1\",\n \"foo2\": \"bar2\"\n },\n \"headers\": {\n \"x-forwarded-proto\": \"https\",\n \"host\": \"postman-echo.com\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"5c27cd7d-6b16-4e5a-a0ef-191c9a3a275f\",\n \"user-agent\": \"PostmanRuntime/7.6.1\",\n \"x-forwarded-port\": \"443\"\n },\n \"url\": \"https://postman-echo.com/get?foo1=bar1&foo2=bar2\"\n}" 115 | } 116 | ] 117 | } 118 | ] 119 | } 120 | -------------------------------------------------------------------------------- /tests/fixtures/graphql.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "bff8cb92-c6f1-465a-a0c6-43d233ab0a37", 4 | "name": "Star Wars GraphQL API - documentation", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "10825352" 7 | }, 8 | "item": [ 9 | { 10 | "name": "queries", 11 | "item": [ 12 | { 13 | "name": "hero", 14 | "request": { 15 | "method": "POST", 16 | "header": [], 17 | "body": { 18 | "mode": "graphql", 19 | "graphql": { 20 | "query": "query hero ($episode: Episode!) {\n hero (episode: $episode) {\n id\n name\n friends {\n id\n name\n friends {\n id\n name\n appearsIn\n }\n appearsIn\n }\n appearsIn\n }\n}", 21 | "variables": "{\n \"episode\": \"\"\n}" 22 | } 23 | }, 24 | "url": { 25 | "raw": "{{url}}", 26 | "host": ["{{url}}"] 27 | } 28 | }, 29 | "response": [] 30 | }, 31 | { 32 | "name": "human", 33 | "request": { 34 | "method": "POST", 35 | "header": [], 36 | "body": { 37 | "mode": "graphql", 38 | "graphql": { 39 | "query": "query human ($id: String!) {\n human (id: $id) {\n id\n name\n friends {\n id\n name\n friends {\n id\n name\n appearsIn\n }\n appearsIn\n }\n appearsIn\n homePlanet\n }\n}", 40 | "variables": "{\n \"id\": \"\"\n}" 41 | } 42 | }, 43 | "url": { 44 | "raw": "{{url}}", 45 | "host": ["{{url}}"] 46 | } 47 | }, 48 | "response": [] 49 | }, 50 | { 51 | "name": "humans", 52 | "request": { 53 | "method": "POST", 54 | "header": [], 55 | "body": { 56 | "mode": "graphql", 57 | "graphql": { 58 | "query": "query humans ($after: String, $before: String, $first: Int, $last: Int) {\n humans (after: $after, before: $before, first: $first, last: $last) {\n pageInfo {\n hasPreviousPage\n hasNextPage\n startCursor\n endCursor\n }\n edges {\n node {\n id\n name\n friends {\n id\n name\n friends {\n id\n name\n appearsIn\n }\n appearsIn\n }\n appearsIn\n homePlanet\n }\n cursor\n }\n }\n}", 59 | "variables": "{\n \"after\": \"\",\n \"before\": \"\",\n \"first\": 0,\n \"last\": 0\n}" 60 | } 61 | }, 62 | "url": { 63 | "raw": "{{url}}", 64 | "host": ["{{url}}"] 65 | } 66 | }, 67 | "response": [] 68 | }, 69 | { 70 | "name": "droid", 71 | "request": { 72 | "method": "POST", 73 | "header": [], 74 | "body": { 75 | "mode": "graphql", 76 | "graphql": { 77 | "query": "query droid ($id: String!) {\n droid (id: $id) {\n id\n name\n friends {\n id\n name\n friends {\n id\n name\n appearsIn\n }\n appearsIn\n }\n appearsIn\n primaryFunction\n }\n}", 78 | "variables": "{\n \"id\": \"\"\n}" 79 | } 80 | }, 81 | "url": { 82 | "raw": "{{url}}", 83 | "host": ["{{url}}"] 84 | } 85 | }, 86 | "response": [] 87 | }, 88 | { 89 | "name": "droids", 90 | "request": { 91 | "method": "POST", 92 | "header": [], 93 | "body": { 94 | "mode": "graphql", 95 | "graphql": { 96 | "query": "query droids ($after: String, $before: String, $first: Int, $last: Int) {\n droids (after: $after, before: $before, first: $first, last: $last) {\n pageInfo {\n hasPreviousPage\n hasNextPage\n startCursor\n endCursor\n }\n edges {\n node {\n id\n name\n friends {\n id\n name\n friends {\n id\n name\n appearsIn\n }\n appearsIn\n }\n appearsIn\n primaryFunction\n }\n cursor\n }\n }\n}", 97 | "variables": "{\n \"after\": \"\",\n \"before\": \"\",\n \"first\": 0,\n \"last\": 0\n}" 98 | } 99 | }, 100 | "url": { 101 | "raw": "{{url}}", 102 | "host": ["{{url}}"] 103 | } 104 | }, 105 | "response": [] 106 | } 107 | ] 108 | } 109 | ], 110 | "variable": [ 111 | { 112 | "key": "url", 113 | "value": "https://swapi-graphql.netlify.app/.netlify/functions/index", 114 | "type": "any" 115 | } 116 | ] 117 | } 118 | -------------------------------------------------------------------------------- /tests/fixtures/noauth.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "2a763711-fdbf-4c82-a7eb-f27c3db887e7", 4 | "name": "OAuth Example", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "updatedAt": "2023-11-22T17:23:18.000Z", 7 | "uid": "10354132-2a763711-fdbf-4c82-a7eb-f27c3db887e7" 8 | }, 9 | "item": [], 10 | "auth": { 11 | "type": "noauth" 12 | }, 13 | "event": [ 14 | { 15 | "listen": "prerequest", 16 | "script": { 17 | "id": "bbaa0257-914e-4fc3-bb54-422263e7da80", 18 | "type": "text/javascript", 19 | "exec": [""] 20 | } 21 | }, 22 | { 23 | "listen": "test", 24 | "script": { 25 | "id": "d5bbc401-c441-4db1-b80c-23e5fd06b8d3", 26 | "type": "text/javascript", 27 | "exec": [""] 28 | } 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tests/fixtures/oauth2-code.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "2a763711-fdbf-4c82-a7eb-f27c3db887e7", 4 | "name": "OAuth Example", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "updatedAt": "2023-11-22T17:23:18.000Z", 7 | "uid": "10354132-2a763711-fdbf-4c82-a7eb-f27c3db887e7" 8 | }, 9 | "item": [], 10 | "auth": { 11 | "type": "oauth2", 12 | "oauth2": [ 13 | { 14 | "key": "tokenName", 15 | "value": "avcd", 16 | "type": "string" 17 | }, 18 | { 19 | "key": "state", 20 | "value": "1234", 21 | "type": "string" 22 | }, 23 | { 24 | "key": "scope", 25 | "value": "a:b", 26 | "type": "string" 27 | }, 28 | { 29 | "key": "clientSecret", 30 | "value": "abc", 31 | "type": "string" 32 | }, 33 | { 34 | "key": "clientId", 35 | "value": "abc", 36 | "type": "string" 37 | }, 38 | { 39 | "key": "accessTokenUrl", 40 | "value": "https://example.com/oauth2/token", 41 | "type": "string" 42 | }, 43 | { 44 | "key": "authUrl", 45 | "value": "https://example.com/oauth2/authorization", 46 | "type": "string" 47 | }, 48 | { 49 | "key": "addTokenTo", 50 | "value": "header", 51 | "type": "string" 52 | } 53 | ] 54 | }, 55 | "event": [ 56 | { 57 | "listen": "prerequest", 58 | "script": { 59 | "id": "bbaa0257-914e-4fc3-bb54-422263e7da80", 60 | "type": "text/javascript", 61 | "exec": [""] 62 | } 63 | }, 64 | { 65 | "listen": "test", 66 | "script": { 67 | "id": "d5bbc401-c441-4db1-b80c-23e5fd06b8d3", 68 | "type": "text/javascript", 69 | "exec": [""] 70 | } 71 | } 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /tests/fixtures/only-root-path.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "bff8cb92-c6f1-465a-a0c6-43d233ab0a37", 4 | "name": "Star Wars GraphQL API - documentation", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "10825352" 7 | }, 8 | "item": [ 9 | { 10 | "name": "queries", 11 | "item": [ 12 | { 13 | "name": "hero", 14 | "request": { 15 | "method": "POST", 16 | "header": [], 17 | "body": {}, 18 | "url": { 19 | "raw": "{{url}}", 20 | "host": ["{{url}}"] 21 | } 22 | }, 23 | "response": [] 24 | }, 25 | { 26 | "name": "human", 27 | "request": { 28 | "method": "POST", 29 | "header": [], 30 | "body": {}, 31 | "url": { 32 | "raw": "{{url}}", 33 | "host": ["{{url}}"] 34 | } 35 | }, 36 | "response": [] 37 | }, 38 | { 39 | "name": "humans", 40 | "request": { 41 | "method": "POST", 42 | "header": [], 43 | "body": {}, 44 | "url": { 45 | "raw": "{{url}}", 46 | "host": ["{{url}}"] 47 | } 48 | }, 49 | "response": [] 50 | }, 51 | { 52 | "name": "droid", 53 | "request": { 54 | "method": "POST", 55 | "header": [], 56 | "body": {}, 57 | "url": { 58 | "raw": "{{url}}", 59 | "host": ["{{url}}"] 60 | } 61 | }, 62 | "response": [] 63 | }, 64 | { 65 | "name": "droids", 66 | "request": { 67 | "method": "POST", 68 | "header": [], 69 | "body": {}, 70 | "url": { 71 | "raw": "{{url}}", 72 | "host": ["{{url}}"] 73 | } 74 | }, 75 | "response": [] 76 | } 77 | ] 78 | } 79 | ], 80 | "variable": [ 81 | { 82 | "key": "url", 83 | "value": "https://swapi-graphql.netlify.app/.netlify/functions/index", 84 | "type": "any" 85 | } 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /tests/fixtures/todo.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "4f3c0bbe-2592-4fe9-937f-a77367b4040a", 4 | "name": "Todo Live Collection", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", 6 | "_exporter_id": "10354132", 7 | "_collection_link": "https://www.postman.com/technology-demo/workspace/integrations-nova-security-api/collection/23437215-4f3c0bbe-2592-4fe9-937f-a77367b4040a?action=share&source=collection_link&creator=10354132" 8 | }, 9 | "item": [ 10 | { 11 | "name": "/todos", 12 | "request": { 13 | "method": "GET", 14 | "header": [ 15 | { 16 | "key": "if-none-match", 17 | "value": "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" 18 | }, 19 | { 20 | "key": "cache-control", 21 | "value": "no-cache", 22 | "disabled": true 23 | } 24 | ], 25 | "url": { 26 | "raw": "{{url}}/todos", 27 | "host": [ 28 | "{{url}}" 29 | ], 30 | "path": [ 31 | "todos" 32 | ] 33 | } 34 | }, 35 | "response": [ 36 | { 37 | "name": "304 Not Modified", 38 | "originalRequest": { 39 | "method": "GET", 40 | "header": [ 41 | { 42 | "key": "if-none-match", 43 | "value": "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" 44 | } 45 | ], 46 | "url": { 47 | "raw": "{{url}}/todos", 48 | "host": [ 49 | "{{url}}" 50 | ], 51 | "path": [ 52 | "todos" 53 | ] 54 | } 55 | }, 56 | "status": "Not Modified", 57 | "code": 304, 58 | "_postman_previewlanguage": "json", 59 | "header": [ 60 | { 61 | "key": "x-powered-by", 62 | "value": "Express" 63 | }, 64 | { 65 | "key": "etag", 66 | "value": "W/\"2-l9Fw4VUO7kr8CvBlt4zaMCqXZ0w\"" 67 | } 68 | ], 69 | "cookie": [], 70 | "body": "{}" 71 | }, 72 | { 73 | "name": "200 OK", 74 | "originalRequest": { 75 | "method": "GET", 76 | "header": [], 77 | "url": { 78 | "raw": "{{url}}/todos", 79 | "host": [ 80 | "{{url}}" 81 | ], 82 | "path": [ 83 | "todos" 84 | ] 85 | } 86 | }, 87 | "status": "OK", 88 | "code": 200, 89 | "_postman_previewlanguage": "json", 90 | "header": [ 91 | { 92 | "key": "x-powered-by", 93 | "value": "Express" 94 | }, 95 | { 96 | "key": "content-type", 97 | "value": "application/json; charset=utf-8" 98 | }, 99 | { 100 | "key": "content-length", 101 | "value": "55" 102 | }, 103 | { 104 | "key": "etag", 105 | "value": "W/\"37-rCbCk6sMHKufxTlxOZqO2yaOzkw\"" 106 | } 107 | ], 108 | "cookie": [], 109 | "body": "[\n {\n \"id\": 1,\n \"title\": \"create a new app\",\n \"completed\": false\n }\n]" 110 | } 111 | ] 112 | }, 113 | { 114 | "name": "/todos", 115 | "request": { 116 | "method": "POST", 117 | "header": [ 118 | { 119 | "key": "origin", 120 | "value": "http://localhost:3000" 121 | }, 122 | { 123 | "key": "cache-control", 124 | "value": "no-cache", 125 | "disabled": true 126 | } 127 | ], 128 | "body": { 129 | "mode": "raw", 130 | "raw": "{\n \"title\": \"create a new app\"\n}", 131 | "options": { 132 | "raw": { 133 | "language": "json" 134 | } 135 | } 136 | }, 137 | "url": { 138 | "raw": "{{url}}/todos", 139 | "host": [ 140 | "{{url}}" 141 | ], 142 | "path": [ 143 | "todos" 144 | ] 145 | } 146 | }, 147 | "response": [ 148 | { 149 | "name": "201 Created", 150 | "originalRequest": { 151 | "method": "POST", 152 | "header": [ 153 | { 154 | "key": "origin", 155 | "value": "http://localhost:3000" 156 | } 157 | ], 158 | "body": { 159 | "mode": "raw", 160 | "raw": "{\n \"title\": \"create a new app\"\n}", 161 | "options": { 162 | "raw": { 163 | "language": "json" 164 | } 165 | } 166 | }, 167 | "url": { 168 | "raw": "{{url}}/todos", 169 | "host": [ 170 | "{{url}}" 171 | ], 172 | "path": [ 173 | "todos" 174 | ] 175 | } 176 | }, 177 | "status": "Created", 178 | "code": 201, 179 | "_postman_previewlanguage": "json", 180 | "header": [ 181 | { 182 | "key": "x-powered-by", 183 | "value": "Express" 184 | }, 185 | { 186 | "key": "content-type", 187 | "value": "application/json; charset=utf-8" 188 | }, 189 | { 190 | "key": "content-length", 191 | "value": "53" 192 | }, 193 | { 194 | "key": "etag", 195 | "value": "W/\"35-pt9AScFLjaMH6u4fQd2srp9JL88\"" 196 | } 197 | ], 198 | "cookie": [], 199 | "body": "{\n \"id\": 1,\n \"title\": \"create a new app\",\n \"completed\": false\n}" 200 | } 201 | ] 202 | }, 203 | { 204 | "name": "/todos/:id", 205 | "request": { 206 | "method": "GET", 207 | "header": [], 208 | "url": { 209 | "raw": "{{url}}/todos/:id", 210 | "host": [ 211 | "{{url}}" 212 | ], 213 | "path": [ 214 | "todos", 215 | ":id" 216 | ], 217 | "variable": [ 218 | { 219 | "key": "id", 220 | "value": "1" 221 | } 222 | ] 223 | } 224 | }, 225 | "response": [ 226 | { 227 | "name": "200 OK", 228 | "originalRequest": { 229 | "method": "GET", 230 | "header": [], 231 | "url": { 232 | "raw": "{{url}}/todos/:id", 233 | "host": [ 234 | "{{url}}" 235 | ], 236 | "path": [ 237 | "todos", 238 | ":id" 239 | ], 240 | "variable": [ 241 | { 242 | "key": "id", 243 | "value": "1" 244 | } 245 | ] 246 | } 247 | }, 248 | "status": "OK", 249 | "code": 200, 250 | "_postman_previewlanguage": "json", 251 | "header": [ 252 | { 253 | "key": "x-powered-by", 254 | "value": "Express" 255 | }, 256 | { 257 | "key": "content-type", 258 | "value": "application/json; charset=utf-8" 259 | }, 260 | { 261 | "key": "content-length", 262 | "value": "53" 263 | }, 264 | { 265 | "key": "etag", 266 | "value": "W/\"35-pt9AScFLjaMH6u4fQd2srp9JL88\"" 267 | } 268 | ], 269 | "cookie": [], 270 | "body": "{\n \"id\": 1,\n \"title\": \"create a new app\",\n \"completed\": false\n}" 271 | }, 272 | { 273 | "name": "404 Not Found", 274 | "originalRequest": { 275 | "method": "GET", 276 | "header": [ 277 | { 278 | "key": "cache-control", 279 | "value": "no-cache" 280 | } 281 | ], 282 | "url": { 283 | "raw": "{{url}}/todos/:id", 284 | "host": [ 285 | "{{url}}" 286 | ], 287 | "path": [ 288 | "todos", 289 | ":id" 290 | ], 291 | "variable": [ 292 | { 293 | "key": "id", 294 | "value": "1" 295 | } 296 | ] 297 | } 298 | }, 299 | "status": "Not Found", 300 | "code": 404, 301 | "_postman_previewlanguage": "json", 302 | "header": [ 303 | { 304 | "key": "x-powered-by", 305 | "value": "Express" 306 | }, 307 | { 308 | "key": "content-type", 309 | "value": "application/json; charset=utf-8" 310 | }, 311 | { 312 | "key": "content-length", 313 | "value": "26" 314 | }, 315 | { 316 | "key": "etag", 317 | "value": "W/\"1a-hwodwbWGyVn+6gaESAkhR6ThSk4\"" 318 | } 319 | ], 320 | "cookie": [], 321 | "body": "{\n \"error\": \"Todo not found\"\n}" 322 | } 323 | ] 324 | }, 325 | { 326 | "name": "/todos/:id", 327 | "request": { 328 | "method": "PUT", 329 | "header": [ 330 | { 331 | "key": "origin", 332 | "value": "http://localhost:3000" 333 | }, 334 | { 335 | "key": "cache-control", 336 | "value": "no-cache", 337 | "disabled": true 338 | } 339 | ], 340 | "body": { 341 | "mode": "raw", 342 | "raw": "{\n \"id\": 1,\n \"title\": \"create a new app\",\n \"completed\": true\n}", 343 | "options": { 344 | "raw": { 345 | "language": "json" 346 | } 347 | } 348 | }, 349 | "url": { 350 | "raw": "{{url}}/todos/:id", 351 | "host": [ 352 | "{{url}}" 353 | ], 354 | "path": [ 355 | "todos", 356 | ":id" 357 | ], 358 | "variable": [ 359 | { 360 | "key": "id", 361 | "value": "1" 362 | } 363 | ] 364 | } 365 | }, 366 | "response": [ 367 | { 368 | "name": "200 OK", 369 | "originalRequest": { 370 | "method": "PUT", 371 | "header": [ 372 | { 373 | "key": "origin", 374 | "value": "http://localhost:3000" 375 | } 376 | ], 377 | "body": { 378 | "mode": "raw", 379 | "raw": "{\n \"id\": 1,\n \"title\": \"create a new app\",\n \"completed\": true\n}", 380 | "options": { 381 | "raw": { 382 | "language": "json" 383 | } 384 | } 385 | }, 386 | "url": { 387 | "raw": "{{url}}/todos/:id", 388 | "host": [ 389 | "{{url}}" 390 | ], 391 | "path": [ 392 | "todos", 393 | ":id" 394 | ], 395 | "variable": [ 396 | { 397 | "key": "id", 398 | "value": "1" 399 | } 400 | ] 401 | } 402 | }, 403 | "status": "OK", 404 | "code": 200, 405 | "_postman_previewlanguage": "json", 406 | "header": [ 407 | { 408 | "key": "x-powered-by", 409 | "value": "Express" 410 | }, 411 | { 412 | "key": "content-type", 413 | "value": "application/json; charset=utf-8" 414 | }, 415 | { 416 | "key": "content-length", 417 | "value": "52" 418 | }, 419 | { 420 | "key": "etag", 421 | "value": "W/\"34-jFAjlX/euuAQycOGrGm2Lfnu/Ws\"" 422 | } 423 | ], 424 | "cookie": [], 425 | "body": "{\n \"id\": 1,\n \"title\": \"create a new app\",\n \"completed\": true\n}" 426 | } 427 | ] 428 | }, 429 | { 430 | "name": "/todos/:id", 431 | "request": { 432 | "method": "DELETE", 433 | "header": [ 434 | { 435 | "key": "origin", 436 | "value": "http://localhost:3000" 437 | }, 438 | { 439 | "key": "cache-control", 440 | "value": "no-cache", 441 | "disabled": true 442 | } 443 | ], 444 | "url": { 445 | "raw": "{{url}}/todos/:id", 446 | "host": [ 447 | "{{url}}" 448 | ], 449 | "path": [ 450 | "todos", 451 | ":id" 452 | ], 453 | "variable": [ 454 | { 455 | "key": "id", 456 | "value": "1" 457 | } 458 | ] 459 | } 460 | }, 461 | "response": [ 462 | { 463 | "name": "204 No Content", 464 | "originalRequest": { 465 | "method": "DELETE", 466 | "header": [ 467 | { 468 | "key": "origin", 469 | "value": "http://localhost:3000" 470 | } 471 | ], 472 | "url": { 473 | "raw": "{{url}}/todos/:id", 474 | "host": [ 475 | "{{url}}" 476 | ], 477 | "path": [ 478 | "todos", 479 | ":id" 480 | ], 481 | "variable": [ 482 | { 483 | "key": "id", 484 | "value": "3" 485 | } 486 | ] 487 | } 488 | }, 489 | "status": "No Content", 490 | "code": 204, 491 | "_postman_previewlanguage": "json", 492 | "header": [ 493 | { 494 | "key": "x-powered-by", 495 | "value": "Express" 496 | } 497 | ], 498 | "cookie": [], 499 | "body": "{}" 500 | }, 501 | { 502 | "name": "404 Not Found", 503 | "originalRequest": { 504 | "method": "DELETE", 505 | "header": [ 506 | { 507 | "key": "origin", 508 | "value": "http://localhost:3000" 509 | }, 510 | { 511 | "key": "cache-control", 512 | "value": "no-cache" 513 | } 514 | ], 515 | "url": { 516 | "raw": "{{url}}/todos/:id", 517 | "host": [ 518 | "{{url}}" 519 | ], 520 | "path": [ 521 | "todos", 522 | ":id" 523 | ], 524 | "variable": [ 525 | { 526 | "key": "id", 527 | "value": "3" 528 | } 529 | ] 530 | } 531 | }, 532 | "status": "Not Found", 533 | "code": 404, 534 | "_postman_previewlanguage": "json", 535 | "header": [ 536 | { 537 | "key": "x-powered-by", 538 | "value": "Express" 539 | }, 540 | { 541 | "key": "content-type", 542 | "value": "application/json; charset=utf-8" 543 | }, 544 | { 545 | "key": "content-length", 546 | "value": "26" 547 | }, 548 | { 549 | "key": "etag", 550 | "value": "W/\"1a-hwodwbWGyVn+6gaESAkhR6ThSk4\"" 551 | } 552 | ], 553 | "cookie": [], 554 | "body": "{\n \"error\": \"Todo not found\"\n}" 555 | } 556 | ] 557 | }, 558 | { 559 | "name": "/", 560 | "request": { 561 | "method": "GET", 562 | "header": [ 563 | { 564 | "key": "upgrade-insecure-requests", 565 | "value": "1" 566 | }, 567 | { 568 | "key": "if-none-match", 569 | "value": "W/\"d24-18a9987fad5\"" 570 | }, 571 | { 572 | "key": "cache-control", 573 | "value": "no-cache", 574 | "disabled": true 575 | } 576 | ], 577 | "url": { 578 | "raw": "{{url}}/", 579 | "host": [ 580 | "{{url}}" 581 | ], 582 | "path": [ 583 | "" 584 | ] 585 | } 586 | }, 587 | "response": [ 588 | { 589 | "name": "304 Not Modified", 590 | "originalRequest": { 591 | "method": "GET", 592 | "header": [ 593 | { 594 | "key": "upgrade-insecure-requests", 595 | "value": "1" 596 | }, 597 | { 598 | "key": "if-none-match", 599 | "value": "W/\"d24-18a9987fad5\"" 600 | } 601 | ], 602 | "url": { 603 | "raw": "{{url}}/", 604 | "host": [ 605 | "{{url}}" 606 | ], 607 | "path": [ 608 | "" 609 | ] 610 | } 611 | }, 612 | "status": "Not Modified", 613 | "code": 304, 614 | "_postman_previewlanguage": "json", 615 | "header": [ 616 | { 617 | "key": "x-powered-by", 618 | "value": "Express" 619 | }, 620 | { 621 | "key": "accept-ranges", 622 | "value": "bytes" 623 | }, 624 | { 625 | "key": "cache-control", 626 | "value": "public, max-age=0" 627 | }, 628 | { 629 | "key": "last-modified", 630 | "value": "Fri, 15 Sep 2023 15:49:00 GMT" 631 | }, 632 | { 633 | "key": "etag", 634 | "value": "W/\"d24-18a9987fad5\"" 635 | } 636 | ], 637 | "cookie": [], 638 | "body": "{}" 639 | }, 640 | { 641 | "name": "200 OK", 642 | "originalRequest": { 643 | "method": "GET", 644 | "header": [ 645 | { 646 | "key": "upgrade-insecure-requests", 647 | "value": "1" 648 | }, 649 | { 650 | "key": "if-none-match", 651 | "value": "W/\"d24-18a9987fad5\"" 652 | }, 653 | { 654 | "key": "cache-control", 655 | "value": "no-cache" 656 | } 657 | ], 658 | "url": { 659 | "raw": "{{url}}/", 660 | "host": [ 661 | "{{url}}" 662 | ], 663 | "path": [ 664 | "" 665 | ] 666 | } 667 | }, 668 | "status": "OK", 669 | "code": 200, 670 | "_postman_previewlanguage": "json", 671 | "header": [ 672 | { 673 | "key": "x-powered-by", 674 | "value": "Express" 675 | }, 676 | { 677 | "key": "accept-ranges", 678 | "value": "bytes" 679 | }, 680 | { 681 | "key": "cache-control", 682 | "value": "public, max-age=0" 683 | }, 684 | { 685 | "key": "last-modified", 686 | "value": "Fri, 15 Sep 2023 15:49:00 GMT" 687 | }, 688 | { 689 | "key": "etag", 690 | "value": "W/\"d24-18a9987fad5\"" 691 | }, 692 | { 693 | "key": "content-type", 694 | "value": "text/html; charset=UTF-8" 695 | }, 696 | { 697 | "key": "content-length", 698 | "value": 3364 699 | } 700 | ], 701 | "cookie": [], 702 | "body": "{}" 703 | } 704 | ] 705 | } 706 | ], 707 | "variable": [ 708 | { 709 | "key": "url", 710 | "value": "localhost:3000" 711 | } 712 | ] 713 | } 714 | -------------------------------------------------------------------------------- /tests/fixtures/users.postman.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "bb4eaba5-dc18-42e1-8d78-8acf16208017", 4 | "name": "Users API - documentation", 5 | "description": "This is a sample OpenAPI definition for a Users API.\n\nContact Support:\n Name: Kevin Swiber\n Email: kswiber@gmail.com", 6 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 7 | }, 8 | "item": [ 9 | { 10 | "name": "users", 11 | "item": [ 12 | { 13 | "name": "List Users", 14 | "request": { 15 | "method": "GET", 16 | "header": [], 17 | "url": { 18 | "raw": "{{baseUrl}}/users", 19 | "host": [ 20 | "{{baseUrl}}" 21 | ], 22 | "path": [ 23 | "users" 24 | ] 25 | }, 26 | "description": "Returns a list of users." 27 | }, 28 | "response": [ 29 | { 30 | "name": "A list of users.", 31 | "originalRequest": { 32 | "method": "GET", 33 | "header": [], 34 | "url": { 35 | "raw": "{{baseUrl}}/users", 36 | "host": [ 37 | "{{baseUrl}}" 38 | ], 39 | "path": [ 40 | "users" 41 | ] 42 | } 43 | }, 44 | "status": "OK", 45 | "code": 200, 46 | "_postman_previewlanguage": "json", 47 | "header": [ 48 | { 49 | "key": "Content-Type", 50 | "value": "application/json" 51 | } 52 | ], 53 | "cookie": [], 54 | "body": "{\n \"page\": 1,\n \"per_page\": 10,\n \"total\": 30,\n \"total_pages\": 3,\n \"data\": [\n {\n \"id\": 12345,\n \"email\": \"blah@example.com\",\n \"first_name\": \"George\",\n \"last_name\": \"Burns\",\n \"avatar\": \"https://george.burns/avatar\"\n }\n ]\n}" 55 | } 56 | ] 57 | }, 58 | { 59 | "name": "Get a Single User", 60 | "request": { 61 | "method": "GET", 62 | "header": [], 63 | "url": { 64 | "raw": "{{baseUrl}}/users/:userId", 65 | "host": [ 66 | "{{baseUrl}}" 67 | ], 68 | "path": [ 69 | "users", 70 | ":userId" 71 | ], 72 | "variable": [ 73 | { 74 | "id": "23523928-bd5b-4128-ac91-4393b93ca655", 75 | "key": "userId", 76 | "value": "-52523638", 77 | "type": "string", 78 | "description": "(Required) The ID of the user." 79 | } 80 | ] 81 | }, 82 | "description": "Returns a single user." 83 | }, 84 | "response": [ 85 | { 86 | "name": "A single user.", 87 | "originalRequest": { 88 | "method": "GET", 89 | "header": [], 90 | "url": { 91 | "raw": "{{baseUrl}}/users/:userId", 92 | "host": [ 93 | "{{baseUrl}}" 94 | ], 95 | "path": [ 96 | "users", 97 | ":userId" 98 | ], 99 | "variable": [ 100 | { 101 | "key": "userId" 102 | } 103 | ] 104 | } 105 | }, 106 | "status": "OK", 107 | "code": 200, 108 | "_postman_previewlanguage": "json", 109 | "header": [ 110 | { 111 | "key": "Content-Type", 112 | "value": "application/json" 113 | } 114 | ], 115 | "cookie": [], 116 | "body": "{\n \"id\": 12345,\n \"email\": \"blah@example.com\",\n \"first_name\": \"George\",\n \"last_name\": \"Burns\",\n \"avatar\": \"https://george.burns/avatar\"\n}" 117 | } 118 | ] 119 | } 120 | ], 121 | "description": "User management operations.", 122 | "protocolProfileBehavior": {} 123 | } 124 | ], 125 | "variable": [ 126 | { 127 | "id": "baseUrl", 128 | "key": "baseUrl", 129 | "value": "https://reqres.in/api", 130 | "type": "string" 131 | } 132 | ], 133 | "protocolProfileBehavior": {} 134 | } -------------------------------------------------------------------------------- /tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | macro_rules! test_fixture { 2 | ($name:ident, $filename:expr) => { 3 | #[test] 4 | fn $name() { 5 | let filename = get_fixture($filename); 6 | let options = postman2openapi::TranspileOptions { 7 | format: postman2openapi::TargetFormat::Json, 8 | }; 9 | match postman2openapi::from_path(&filename, options) { 10 | Ok(_oas) => assert!(true), 11 | Err(err) => assert!(false, "{:?}", err), 12 | } 13 | } 14 | }; 15 | } 16 | 17 | test_fixture!(it_parses_github_api_collection, "github.postman.json"); 18 | test_fixture!(it_parses_postman_api_collection, "postman-api.postman.json"); 19 | test_fixture!(it_parses_pdf_co_collection, "pdfco.postman.json"); 20 | test_fixture!(it_parses_postman_echo_collection, "echo.postman.json"); 21 | test_fixture!(it_parses_twitter_api_collection, "twitter-api.postman.json"); 22 | test_fixture!(it_parses_fastly_api_collection, "fastly.postman.json"); 23 | test_fixture!(it_parses_users_api_collection, "users.postman.json"); 24 | test_fixture!(it_parses_graphql_api_collection, "graphql.postman.json"); 25 | test_fixture!(it_parses_todo_api_collection, "todo.postman.json"); 26 | test_fixture!( 27 | it_parses_gotomeeting_api_collection, 28 | "gotomeeting.postman.json" 29 | ); 30 | test_fixture!( 31 | it_parses_calculator_soap_collection, 32 | "calculator-soap.postman.json" 33 | ); 34 | test_fixture!(it_parses_oauth2_code_collection, "oauth2-code.postman.json"); 35 | test_fixture!(it_parses_api_key_collection, "api-key.postman.json"); 36 | test_fixture!( 37 | it_parses_empty_header_object_collection, 38 | "empty-header-object.postman.json" 39 | ); 40 | 41 | fn get_fixture(filename: &str) -> String { 42 | let filename: std::path::PathBuf = [env!("CARGO_MANIFEST_DIR"), "./tests/fixtures/", filename] 43 | .iter() 44 | .collect(); 45 | filename.into_os_string().into_string().unwrap() 46 | } 47 | -------------------------------------------------------------------------------- /tests/wasm_browser.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | #[cfg(target_arch = "wasm32")] 3 | mod wasm_browser { 4 | use js_sys::JSON; 5 | use wasm_bindgen_test::*; 6 | wasm_bindgen_test_configure!(run_in_browser); 7 | 8 | #[wasm_bindgen_test] 9 | fn it_transpiles() { 10 | let collection: &'static str = include_str!("./fixtures/wasm/collection.json"); 11 | let openapi: &'static str = include_str!("./fixtures/wasm/openapi.json"); 12 | 13 | match postman2openapi::transpile(JSON::parse(collection).unwrap()) { 14 | Ok(oas) => assert_eq!( 15 | JSON::stringify(&JSON::parse(openapi).unwrap()).unwrap(), 16 | JSON::stringify(&oas).unwrap() 17 | ), 18 | Err(_) => assert!(false), 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/wasm_node.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | #[cfg(target_arch = "wasm32")] 3 | mod wasm_node { 4 | use js_sys::JSON; 5 | use wasm_bindgen_test::*; 6 | 7 | #[wasm_bindgen_test] 8 | fn it_transpiles() { 9 | let collection: &'static str = include_str!("./fixtures/wasm/collection.json"); 10 | let openapi: &'static str = include_str!("./fixtures/wasm/openapi.json"); 11 | 12 | match postman2openapi::transpile(JSON::parse(collection).unwrap()) { 13 | Ok(oas) => assert_eq!( 14 | JSON::stringify(&JSON::parse(openapi).unwrap()).unwrap(), 15 | JSON::stringify(&oas).unwrap() 16 | ), 17 | Err(_) => assert!(false), 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /web/css/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | direction: ltr; 9 | } 10 | 11 | /* PADDING */ 12 | 13 | .CodeMirror-lines { 14 | padding: 4px 0; /* Vertical padding around content */ 15 | } 16 | .CodeMirror pre { 17 | padding: 0 4px; /* Horizontal padding of content */ 18 | } 19 | 20 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 21 | background-color: white; /* The little square between H and V scrollbars */ 22 | } 23 | 24 | /* GUTTER */ 25 | 26 | .CodeMirror-gutters { 27 | border-right: 1px solid #ddd; 28 | background-color: #f7f7f7; 29 | white-space: nowrap; 30 | } 31 | .CodeMirror-linenumbers {} 32 | .CodeMirror-linenumber { 33 | padding: 0 3px 0 5px; 34 | min-width: 20px; 35 | text-align: right; 36 | color: #999; 37 | white-space: nowrap; 38 | } 39 | 40 | .CodeMirror-guttermarker { color: black; } 41 | .CodeMirror-guttermarker-subtle { color: #999; } 42 | 43 | /* CURSOR */ 44 | 45 | .CodeMirror-cursor { 46 | border-left: 1px solid black; 47 | border-right: none; 48 | width: 0; 49 | } 50 | /* Shown when moving in bi-directional text */ 51 | .CodeMirror div.CodeMirror-secondarycursor { 52 | border-left: 1px solid silver; 53 | } 54 | .cm-fat-cursor .CodeMirror-cursor { 55 | width: auto; 56 | border: 0 !important; 57 | background: #7e7; 58 | } 59 | .cm-fat-cursor div.CodeMirror-cursors { 60 | z-index: 1; 61 | } 62 | .cm-fat-cursor-mark { 63 | background-color: rgba(20, 255, 20, 0.5); 64 | -webkit-animation: blink 1.06s steps(1) infinite; 65 | -moz-animation: blink 1.06s steps(1) infinite; 66 | animation: blink 1.06s steps(1) infinite; 67 | } 68 | .cm-animate-fat-cursor { 69 | width: auto; 70 | border: 0; 71 | -webkit-animation: blink 1.06s steps(1) infinite; 72 | -moz-animation: blink 1.06s steps(1) infinite; 73 | animation: blink 1.06s steps(1) infinite; 74 | background-color: #7e7; 75 | } 76 | @-moz-keyframes blink { 77 | 0% {} 78 | 50% { background-color: transparent; } 79 | 100% {} 80 | } 81 | @-webkit-keyframes blink { 82 | 0% {} 83 | 50% { background-color: transparent; } 84 | 100% {} 85 | } 86 | @keyframes blink { 87 | 0% {} 88 | 50% { background-color: transparent; } 89 | 100% {} 90 | } 91 | 92 | /* Can style cursor different in overwrite (non-insert) mode */ 93 | .CodeMirror-overwrite .CodeMirror-cursor {} 94 | 95 | .cm-tab { display: inline-block; text-decoration: inherit; } 96 | 97 | .CodeMirror-rulers { 98 | position: absolute; 99 | left: 0; right: 0; top: -50px; bottom: -20px; 100 | overflow: hidden; 101 | } 102 | .CodeMirror-ruler { 103 | border-left: 1px solid #ccc; 104 | top: 0; bottom: 0; 105 | position: absolute; 106 | } 107 | 108 | /* DEFAULT THEME */ 109 | 110 | .cm-s-default .cm-header {color: blue;} 111 | .cm-s-default .cm-quote {color: #090;} 112 | .cm-negative {color: #d44;} 113 | .cm-positive {color: #292;} 114 | .cm-header, .cm-strong {font-weight: bold;} 115 | .cm-em {font-style: italic;} 116 | .cm-link {text-decoration: underline;} 117 | .cm-strikethrough {text-decoration: line-through;} 118 | 119 | .cm-s-default .cm-keyword {color: #708;} 120 | .cm-s-default .cm-atom {color: #219;} 121 | .cm-s-default .cm-number {color: #164;} 122 | .cm-s-default .cm-def {color: #00f;} 123 | .cm-s-default .cm-variable, 124 | .cm-s-default .cm-punctuation, 125 | .cm-s-default .cm-property, 126 | .cm-s-default .cm-operator {} 127 | .cm-s-default .cm-variable-2 {color: #05a;} 128 | .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} 129 | .cm-s-default .cm-comment {color: #a50;} 130 | .cm-s-default .cm-string {color: #a11;} 131 | .cm-s-default .cm-string-2 {color: #f50;} 132 | .cm-s-default .cm-meta {color: #555;} 133 | .cm-s-default .cm-qualifier {color: #555;} 134 | .cm-s-default .cm-builtin {color: #30a;} 135 | .cm-s-default .cm-bracket {color: #997;} 136 | .cm-s-default .cm-tag {color: #170;} 137 | .cm-s-default .cm-attribute {color: #00c;} 138 | .cm-s-default .cm-hr {color: #999;} 139 | .cm-s-default .cm-link {color: #00c;} 140 | 141 | .cm-s-default .cm-error {color: #f00;} 142 | .cm-invalidchar {color: #f00;} 143 | 144 | .CodeMirror-composing { border-bottom: 2px solid; } 145 | 146 | /* Default styles for common addons */ 147 | 148 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} 149 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} 150 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 151 | .CodeMirror-activeline-background {background: #e8f2ff;} 152 | 153 | /* STOP */ 154 | 155 | /* The rest of this file contains styles related to the mechanics of 156 | the editor. You probably shouldn't touch them. */ 157 | 158 | .CodeMirror { 159 | position: relative; 160 | overflow: hidden; 161 | background: white; 162 | } 163 | 164 | .CodeMirror-scroll { 165 | overflow: scroll !important; /* Things will break if this is overridden */ 166 | /* 30px is the magic margin used to hide the element's real scrollbars */ 167 | /* See overflow: hidden in .CodeMirror */ 168 | margin-bottom: -30px; margin-right: -30px; 169 | padding-bottom: 30px; 170 | height: 100%; 171 | outline: none; /* Prevent dragging from highlighting the element */ 172 | position: relative; 173 | } 174 | .CodeMirror-sizer { 175 | position: relative; 176 | border-right: 30px solid transparent; 177 | } 178 | 179 | /* The fake, visible scrollbars. Used to force redraw during scrolling 180 | before actual scrolling happens, thus preventing shaking and 181 | flickering artifacts. */ 182 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 183 | position: absolute; 184 | z-index: 6; 185 | display: none; 186 | } 187 | .CodeMirror-vscrollbar { 188 | right: 0; top: 0; 189 | overflow-x: hidden; 190 | overflow-y: scroll; 191 | } 192 | .CodeMirror-hscrollbar { 193 | bottom: 0; left: 0; 194 | overflow-y: hidden; 195 | overflow-x: scroll; 196 | } 197 | .CodeMirror-scrollbar-filler { 198 | right: 0; bottom: 0; 199 | } 200 | .CodeMirror-gutter-filler { 201 | left: 0; bottom: 0; 202 | } 203 | 204 | .CodeMirror-gutters { 205 | position: absolute; left: 0; top: 0; 206 | min-height: 100%; 207 | z-index: 3; 208 | } 209 | .CodeMirror-gutter { 210 | white-space: normal; 211 | height: 100%; 212 | display: inline-block; 213 | vertical-align: top; 214 | margin-bottom: -30px; 215 | } 216 | .CodeMirror-gutter-wrapper { 217 | position: absolute; 218 | z-index: 4; 219 | background: none !important; 220 | border: none !important; 221 | } 222 | .CodeMirror-gutter-background { 223 | position: absolute; 224 | top: 0; bottom: 0; 225 | z-index: 4; 226 | } 227 | .CodeMirror-gutter-elt { 228 | position: absolute; 229 | cursor: default; 230 | z-index: 4; 231 | } 232 | .CodeMirror-gutter-wrapper ::selection { background-color: transparent } 233 | .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } 234 | 235 | .CodeMirror-lines { 236 | cursor: text; 237 | min-height: 1px; /* prevents collapsing before first draw */ 238 | } 239 | .CodeMirror pre { 240 | /* Reset some styles that the rest of the page might have set */ 241 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 242 | border-width: 0; 243 | background: transparent; 244 | font-family: inherit; 245 | font-size: inherit; 246 | margin: 0; 247 | white-space: pre; 248 | word-wrap: normal; 249 | line-height: inherit; 250 | color: inherit; 251 | z-index: 2; 252 | position: relative; 253 | overflow: visible; 254 | -webkit-tap-highlight-color: transparent; 255 | -webkit-font-variant-ligatures: contextual; 256 | font-variant-ligatures: contextual; 257 | } 258 | .CodeMirror-wrap pre { 259 | word-wrap: break-word; 260 | white-space: pre-wrap; 261 | word-break: normal; 262 | } 263 | 264 | .CodeMirror-linebackground { 265 | position: absolute; 266 | left: 0; right: 0; top: 0; bottom: 0; 267 | z-index: 0; 268 | } 269 | 270 | .CodeMirror-linewidget { 271 | position: relative; 272 | z-index: 2; 273 | padding: 0.1px; /* Force widget margins to stay inside of the container */ 274 | } 275 | 276 | .CodeMirror-widget {} 277 | 278 | .CodeMirror-rtl pre { direction: rtl; } 279 | 280 | .CodeMirror-code { 281 | outline: none; 282 | } 283 | 284 | /* Force content-box sizing for the elements where we expect it */ 285 | .CodeMirror-scroll, 286 | .CodeMirror-sizer, 287 | .CodeMirror-gutter, 288 | .CodeMirror-gutters, 289 | .CodeMirror-linenumber { 290 | -moz-box-sizing: content-box; 291 | box-sizing: content-box; 292 | } 293 | 294 | .CodeMirror-measure { 295 | position: absolute; 296 | width: 100%; 297 | height: 0; 298 | overflow: hidden; 299 | visibility: hidden; 300 | } 301 | 302 | .CodeMirror-cursor { 303 | position: absolute; 304 | pointer-events: none; 305 | } 306 | .CodeMirror-measure pre { position: static; } 307 | 308 | div.CodeMirror-cursors { 309 | visibility: hidden; 310 | position: relative; 311 | z-index: 3; 312 | } 313 | div.CodeMirror-dragcursors { 314 | visibility: visible; 315 | } 316 | 317 | .CodeMirror-focused div.CodeMirror-cursors { 318 | visibility: visible; 319 | } 320 | 321 | .CodeMirror-selected { background: #d9d9d9; } 322 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 323 | .CodeMirror-crosshair { cursor: crosshair; } 324 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 325 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 326 | 327 | .cm-searching { 328 | background-color: #ffa; 329 | background-color: rgba(255, 255, 0, .4); 330 | } 331 | 332 | /* Used to force a border model for a node */ 333 | .cm-force-border { padding-right: .1px; } 334 | 335 | @media print { 336 | /* Hide the cursor when printing */ 337 | .CodeMirror div.CodeMirror-cursors { 338 | visibility: hidden; 339 | } 340 | } 341 | 342 | /* See issue #2901 */ 343 | .cm-tab-wrap-hack:after { content: ''; } 344 | 345 | /* Help users use markselection to safely style text background */ 346 | span.CodeMirror-selectedtext { background: none; } 347 | -------------------------------------------------------------------------------- /web/css/demo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | } 4 | 5 | body { 6 | margin: 0 30px; 7 | padding-bottom: 125px; 8 | overflow-x: hidden; 9 | } 10 | 11 | .header { 12 | padding: 1px 0 30px; 13 | white-space: nowrap; 14 | overflow: hidden; 15 | } 16 | 17 | .subheader { 18 | position: absolute; 19 | top: -35px; 20 | width: 100%; 21 | } 22 | 23 | .content { 24 | height: 100%; 25 | } 26 | 27 | #permalink { 28 | float: right; 29 | } 30 | 31 | .src, .dst { 32 | width: 49%; 33 | position: relative; 34 | float: left; 35 | height: 100%; 36 | } 37 | 38 | .dst { 39 | float: right; 40 | } 41 | 42 | .src .CodeMirror { 43 | background-color: #fffffb; 44 | border: 1px solid #888; 45 | border-radius: 3px; 46 | box-shadow: 0 0 3px #ccc inset; 47 | } 48 | 49 | .dst .CodeMirror { 50 | background-color: #F8F8F8; 51 | border: 1px solid #eee; 52 | border-radius: 3px; 53 | } 54 | 55 | /*.CodeMirror-scroll { 56 | height: 500px; 57 | line-height: 1.2; 58 | }*/ 59 | 60 | .CodeMirror { 61 | height: 100%; 62 | font-size: 13px; 63 | } 64 | 65 | .CodeMirror .cm-comment { 66 | color: #baa; 67 | } 68 | 69 | .gh-ribbon { 70 | -webkit-transform: rotate(45deg); 71 | -ms-transform: rotate(45deg); 72 | transform: rotate(45deg); 73 | background-color: #686868; 74 | box-shadow: 0 0 2px rgba(102, 102, 102, 0.4); 75 | display: block; 76 | padding: 1px 0; 77 | position: fixed; 78 | right: -60px; 79 | top: 44px; 80 | width: 230px; 81 | z-index: 10000; 82 | } 83 | .gh-ribbon a { 84 | font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; 85 | font-size: 13px; 86 | border: 1px solid #AAAAAA; 87 | color: #FFFFFF; 88 | display: block; 89 | font-size: 13px; 90 | font-weight: 700; 91 | outline: medium none; 92 | padding: 4px 50px 2px; 93 | text-align: center; 94 | text-decoration: none; 95 | } 96 | .csstransforms .gh-ribbon { 97 | display: block; 98 | } 99 | 100 | 101 | .btn { 102 | border: 1px solid #AAAAAA; 103 | outline: none; 104 | font-size: 13px; 105 | background-color: #FFFFFF; 106 | border-radius: 5px; 107 | font-weight: 600; 108 | position: absolute; 109 | right: 5px; 110 | top:5px; 111 | z-index: 10; 112 | padding: 3px 5px; 113 | } 114 | 115 | .btn:hover { 116 | background-color: #EEEEEE; 117 | } -------------------------------------------------------------------------------- /web/js/index.js: -------------------------------------------------------------------------------- 1 | import('./web.js').catch(console.error); 2 | -------------------------------------------------------------------------------- /web/js/web.js: -------------------------------------------------------------------------------- 1 | import { default as collection } from './collection'; 2 | import CodeMirror from 'codemirror/lib/codemirror.js'; 3 | import { dump } from 'js-yaml'; 4 | import * as postman2openapi from 'postman2openapi'; 5 | import _CodeMirrorStyles from '../css/codemirror.css'; 6 | import _DemoStyles from '../css/demo.css'; 7 | 8 | const openApiCopyBtn = document.getElementById('openapi-copy-btn'); 9 | 10 | const postmanElement = CodeMirror.fromTextArea( 11 | document.getElementById('postman'), 12 | { 13 | lineNumbers: true, 14 | } 15 | ); 16 | 17 | postmanElement.setValue(JSON.stringify(collection, 0, 2)); 18 | 19 | postmanElement.on('change', (_) => { 20 | update(); 21 | }); 22 | 23 | const openapiElement = CodeMirror.fromTextArea( 24 | document.getElementById('openapi'), 25 | { 26 | readOnly: true, 27 | } 28 | ); 29 | 30 | const update = () => { 31 | const postman = postmanElement.getValue(); 32 | try { 33 | const openapi = postman2openapi.transpile(JSON.parse(postman)); 34 | console.log(openapi); 35 | const output = dump(openapi); 36 | openapiElement.setValue(output); 37 | } catch (e) { 38 | openapiElement.setValue(e); 39 | } 40 | }; 41 | 42 | openApiCopyBtn.addEventListener('click', (e) => { 43 | if (window && window.navigator) { 44 | navigator.clipboard.writeText(openapiElement.getValue()).then(() => { 45 | openApiCopyBtn.innerText = 'Copied'; 46 | }); 47 | } 48 | }); 49 | 50 | update(); 51 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postman2openapi-web", 3 | "version": "0.1.0", 4 | "main": "index.js", 5 | "private": true, 6 | "scripts": { 7 | "build": "rimraf dist wasm && NODE_OPTIONS=--openssl-legacy-provider webpack --config webpack.config.js --mode production", 8 | "start": "rimraf dist wasm && NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server --mode development", 9 | "test": "cargo test && wasm-pack test --headless" 10 | }, 11 | "dependencies": { 12 | "clean-webpack-plugin": "^4.0.0", 13 | "codemirror": "^5.57.0", 14 | "js-yaml": "^4.1.0", 15 | "postman2openapi": "file:./wasm" 16 | }, 17 | "devDependencies": { 18 | "@types/codemirror": "0.0.97", 19 | "@wasm-tool/wasm-pack-plugin": "^1.1.0", 20 | "copy-webpack-plugin": "^5.0.3", 21 | "css-loader": "^4.2.2", 22 | "file-loader": "^6.0.0", 23 | "rimraf": "^3.0.0", 24 | "style-loader": "^1.2.1", 25 | "webpack": "^4.42.3", 26 | "webpack-cli": "^3.3.3", 27 | "webpack-dev-server": "^3.7.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /web/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | postman2openapi - Transform Postman Collections into OpenAPI 5 | 6 | 10 | 14 | 15 | 19 | 20 | 21 | 22 |

23 |

Transform Postman Collections into OpenAPI

24 |

postman2openapi

25 |
26 |
27 |
28 |

Postman Collection JSON:

29 | 30 |
31 |
32 |

OpenAPI:

33 | 34 | 35 |
36 |
37 | 38 |
39 | Fork me on GitHub 45 |
46 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /web/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 3 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 4 | const WasmPackPlugin = require('@wasm-tool/wasm-pack-plugin'); 5 | 6 | const dist = path.resolve(__dirname, 'dist'); 7 | 8 | module.exports = { 9 | entry: { 10 | index: './js/index.js', 11 | }, 12 | output: { 13 | path: dist, 14 | filename: '[name].js', 15 | }, 16 | devServer: { 17 | contentBase: dist, 18 | }, 19 | plugins: [ 20 | new CleanWebpackPlugin(), 21 | new CopyWebpackPlugin([path.resolve(__dirname, 'static')]), 22 | new WasmPackPlugin({ 23 | crateDirectory: path.resolve(__dirname, '..'), 24 | outDir: path.resolve(__dirname, 'wasm'), 25 | }), 26 | ], 27 | module: { 28 | rules: [ 29 | { 30 | test: /\.css$/i, 31 | use: ['style-loader', 'css-loader'], 32 | }, 33 | ], 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /webdriver.json: -------------------------------------------------------------------------------- 1 | { 2 | "moz:firefoxOptions": { 3 | "prefs": { 4 | "media.navigator.streams.fake": true, 5 | "media.navigator.permission.disabled": true 6 | }, 7 | "args": [] 8 | }, 9 | "goog:chromeOptions": { 10 | "args": [ 11 | "--use-fake-device-for-media-stream", 12 | "--use-fake-ui-for-media-stream", 13 | "--force-headless-for-tests" 14 | ] 15 | } 16 | } 17 | --------------------------------------------------------------------------------