diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml new file mode 100644 index 00000000000..58e328a0198 --- /dev/null +++ b/.bazelci/presubmit.yml @@ -0,0 +1,22 @@ +--- +platforms: + ubuntu1804: + build_flags: + - "--build_tag_filters=-nolinux" + build_targets: + - "//..." + test_flags: + - "--features=race" + - "--test_tag_filters=-nolinux" + test_targets: + - "//..." + macos: + build_flags: + - "--build_tag_filters=-nomacos" + build_targets: + - "//..." + test_flags: + - "--features=race" + - "--test_tag_filters=-nomacos" + test_targets: + - "//..." diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 00000000000..84197c89467 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +5.3.2 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..2ce6f07d2b1 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +// For format details, see https://aka.ms/devcontainer.json. +{ + "name": "Go", + "build": { + "dockerfile": "../.github/Dockerfile", + "args": { + "NODE_VERSION": "10" + } + }, + "settings": { + "editor.formatOnSave": true, + "go.toolsManagement.checkForUpdates": "local", + "go.useLanguageServer": true, + "go.gopath": "/go", + "go.goroot": "/usr/local/go", + "bazel.buildifierExecutable": "/go/bin/buildifier", + "bazel.buildifierFixOnFormat": true, + "bazel.enableCodeLens": true, + }, + "extensions": [ + "golang.Go", + "bazelbuild.vscode-bazel", + ], + "remoteUser": "vscode" +} diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000000..cbe263961f9 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,3 @@ +# .git-blame-ignore-revs +# Formatted all protobuf files +bc0110188a8ef8e232050c3d9b347198dc83536a diff --git a/.github/Dockerfile b/.github/Dockerfile new file mode 100644 index 00000000000..4ff3a47e25f --- /dev/null +++ b/.github/Dockerfile @@ -0,0 +1,53 @@ +FROM golang:1.19.3 + +ENV NVM_DIR="/usr/local/share/nvm" +ENV NVM_SYMLINK_CURRENT=true \ + PATH=${NVM_DIR}/current/bin:${PATH} + +ARG VSCODE_SCRIPTS_VERSION="v0.193.0" +ARG NODE_VERSION="10" +# Run some common installation scripts for a nicer dev environment. In order: +# Used to create non-root user and update system packages +# https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/common.md +# We use this to install Go tools used by gopls +# https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/go.md +# We use this to install Node +# https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/node.md +RUN apt-get update && \ + wget "https://raw.githubusercontent.com/microsoft/vscode-dev-containers/${VSCODE_SCRIPTS_VERSION}/script-library/common-debian.sh" && \ + chmod +x ./common-debian.sh && \ + ./common-debian.sh false vscode automatic automatic true false && \ + wget "https://raw.githubusercontent.com/microsoft/vscode-dev-containers/${VSCODE_SCRIPTS_VERSION}/script-library/go-debian.sh" && \ + chmod +x ./go-debian.sh && \ + ./go-debian.sh none /usr/local/go /go vscode false true && \ + wget "https://raw.githubusercontent.com/microsoft/vscode-dev-containers/${VSCODE_SCRIPTS_VERSION}/script-library/node-debian.sh" && \ + chmod +x ./node-debian.sh && \ + ./node-debian.sh "${NVM_DIR}" "${NODE_VERSION}" vscode true && \ + rm common-debian.sh go-debian.sh node-debian.sh && \ + DEBIAN_FRONTEND=noninteractive apt-get -y install --no-install-recommends \ + wget \ + unzip \ + openjdk-11-jre \ + bzip2 \ + patch && \ + apt-get clean -y && \ + rm -rf /var/lib/apt/lists/* + +# Install swagger-codegen +ENV SWAGGER_CODEGEN_VERSION=2.4.8 +RUN wget https://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/${SWAGGER_CODEGEN_VERSION}/swagger-codegen-cli-${SWAGGER_CODEGEN_VERSION}.jar \ + -O /usr/local/bin/swagger-codegen-cli.jar && \ + echo '#!/bin/bash\njava -jar /usr/local/bin/swagger-codegen-cli.jar "$@"' > /usr/local/bin/swagger-codegen && \ + chmod +x /usr/local/bin/swagger-codegen + +# Install Bazelisk as bazel to manage Bazel +RUN go install github.com/bazelbuild/bazelisk@latest && \ + mv $(which bazelisk) /usr/local/bin/bazel + +# Install buildifier for bazel formatting +RUN go install github.com/bazelbuild/buildtools/buildifier@latest + +# Give vscode ownership of GOPATH +RUN chown -R vscode: /go + +USER vscode diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..a24c8afffcb --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,66 @@ +# The gRPC-Gateway project is maintained by volunteers in their spare time. Please follow these troubleshooting steps before submitting an issue. + +- [ ] Check if your issue has already been reported (https://github.com/grpc-ecosystem/grpc-gateway/issues). +- [ ] Update your protoc to the [latest version](https://github.com/google/protobuf/releases). +- [ ] Update your copy of the `grpc-gateway` library to the latest version from github: + ```sh + go get github.com/grpc-ecosystem/grpc-gateway/v2@latest + ``` +- [ ] Delete the `protoc-gen-grpc-gateway` and `protoc-gen-openapiv2` binary from your `PATH`, and reinstall the latest versions: + + ```sh + go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway + go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 + ``` + +## I still have a problem! + +Please consider reaching out for help on a chat forum, such as +[Gophers Slack](https://invite.slack.golangbridge.org/) (channel #grpc-gateway). +It's much easier to help with common debugging steps in a chat, and some of +the maintainers are reading the channel regularly. If you +submit an issue which is clearly an environment setup problem, or it's obvious +you haven't tried seeking help somewhere else first, we may close your issue. + +## I still have a problem! + +Please follow these steps to submit a bug report: + +### Bug reports: + +Fill in the following sections with explanations of what's gone wrong. + +### Steps you follow to reproduce the error: + +```html + +``` + +Your steps here. + +### What did you expect to happen instead: + +```html + +``` + +Your answer here. + +### What's your theory on why it isn't working: + +```html + +``` + +Your theory here. diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 00000000000..3c70a7df01e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,28 @@ +--- +name: πŸ› Bug Report +about: Submit a bug report to help us improve +--- + +## πŸ› Bug Report + +(A clear and concise description of what the bug is.) + +## To Reproduce + +(Write your steps here:) + +1. Step 1... +1. Step 2... +1. Step 3... + +## Expected behavior + +(Write what you thought would happen.) + +## Actual Behavior + +(Write what happened. Add screenshots, if applicable.) + +## Your Environment + +(Environment name, version and operating system.) diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 00000000000..d67836feaae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,8 @@ +--- +name: πŸ“š Documentation +about: Report an issue related to documentation +--- + +## πŸ“š Documentation + +(A clear and concise description of what the issue is.) diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md new file mode 100644 index 00000000000..1d9d17e6606 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,8 @@ +--- +name: πŸš€ Feature +about: Submit a proposal/request for a new feature +--- + +## πŸš€ Feature + +(A clear and concise description of what the feature is.) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..2a60298cd1a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ + + +#### References to other Issues or PRs + + + +#### Have you read the [Contributing Guidelines](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md)? + +#### Brief description of what is fixed or changed + +#### Other comments diff --git a/.github/README_GITHUB.md b/.github/README_GITHUB.md new file mode 100644 index 00000000000..5364b0d9e97 --- /dev/null +++ b/.github/README_GITHUB.md @@ -0,0 +1,4 @@ +### Whats up with the Dockerfile? + +The `Dockerfile` in this folder is used as the build environment when regenerating the files (see CONTRIBUTING.md). +The canonical repository for this Dockerfile is `docker.pkg.github.com/grpc-ecosystem/grpc-gateway/build-env`. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..c4740b4d25e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "bundler" # See documentation for possible values + directory: "/docs" # Location of package manifests + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: daily diff --git a/.github/plugins/protoc-gen-grpc-gateway/Dockerfile b/.github/plugins/protoc-gen-grpc-gateway/Dockerfile new file mode 100644 index 00000000000..04c141e7364 --- /dev/null +++ b/.github/plugins/protoc-gen-grpc-gateway/Dockerfile @@ -0,0 +1,25 @@ +FROM golang:1.19.3 as builder + +ARG RELEASE_VERSION + +# Buf plugins must be built for linux/amd64 +ENV GOOS=linux GOARCH=amd64 CGO_ENABLED=0 +RUN go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@${RELEASE_VERSION} + +FROM scratch + +ARG RELEASE_VERSION +ARG GO_PROTOBUF_RELEASE_VERSION +ARG GO_GRPC_RELEASE_VERSION + +# Runtime dependencies +LABEL "build.buf.plugins.runtime_library_versions.0.name"="github.com/grpc-ecosystem/grpc-gateway/v2" +LABEL "build.buf.plugins.runtime_library_versions.0.version"="${RELEASE_VERSION}" +LABEL "build.buf.plugins.runtime_library_versions.1.name"="google.golang.org/protobuf" +LABEL "build.buf.plugins.runtime_library_versions.1.version"="${GO_PROTOBUF_RELEASE_VERSION}" +LABEL "build.buf.plugins.runtime_library_versions.2.name"="google.golang.org/grpc" +LABEL "build.buf.plugins.runtime_library_versions.2.version"="${GO_GRPC_RELEASE_VERSION}" + +COPY --from=builder /go/bin/protoc-gen-grpc-gateway /usr/local/bin/protoc-gen-grpc-gateway + +ENTRYPOINT ["/usr/local/bin/protoc-gen-grpc-gateway"] diff --git a/.github/plugins/protoc-gen-openapiv2/Dockerfile b/.github/plugins/protoc-gen-openapiv2/Dockerfile new file mode 100644 index 00000000000..8c9fcbc9ba6 --- /dev/null +++ b/.github/plugins/protoc-gen-openapiv2/Dockerfile @@ -0,0 +1,13 @@ +FROM golang:1.19.3 as builder + +ARG RELEASE_VERSION + +# Buf plugins must be built for linux/amd64 +ENV GOOS=linux GOARCH=amd64 CGO_ENABLED=0 +RUN go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@${RELEASE_VERSION} + +FROM scratch + +COPY --from=builder /go/bin/protoc-gen-openapiv2 /usr/local/bin/protoc-gen-openapiv2 + +ENTRYPOINT ["/usr/local/bin/protoc-gen-openapiv2"] diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000000..3a1b5318f38 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - help wanted + - enhancement + - security +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..1e37c5536c9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,146 @@ +on: + - pull_request +permissions: + contents: read +name: CI +jobs: + build: + strategy: + matrix: + go-version: [1.17.x, 1.18.x, 1.19.x] + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # tag=v3 + with: + go-version: ${{ matrix.go-version }} + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - run: go build ./... + test: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # tag=v3 + with: + check-latest: true + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - run: go test ./... + node_test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - uses: actions/setup-node@8c91899e586c5b171469028077307d293428b516 # tag=v3 + with: + node-version: 10 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # tag=v3 + with: + check-latest: true + - run: > + cd examples/internal/browser && + npm install gulp-cli && + npm install && + ./node_modules/.bin/gulp + generate: + container: + image: docker.pkg.github.com/grpc-ecosystem/grpc-gateway/build-env:1.19 + options: "--user root" + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - run: make install + - run: make clean + - run: make generate + - run: go mod tidy + - run: git diff --exit-code + bazel: + container: + image: docker.pkg.github.com/grpc-ecosystem/grpc-gateway/build-env:1.19 + options: "--user root" + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # tag=v3 + with: + path: /home/vscode/.cache/_grpc_gateway_bazel + key: v1-bazel-cache-${{ hashFiles('repositories.bzl') }} + restore-keys: v1-bazel-cache- + - name: Configure bazel + run: | + cat > .bazelrc << EOF + startup --output_base /home/vscode/.cache/_grpc_gateway_bazel + build --test_output errors + build --features race + # Workaround https://github.com/bazelbuild/bazel/issues/3645 + # See https://docs.bazel.build/versions/0.23.0/command-line-reference.html + build --local_ram_resources=7168 # Github runners have 7G of memory + build --local_cpu_resources=2 # Github runners have 2 vCPU + EOF + - name: Check that Bazel BUILD files are up-to-date + run: bazel run //:gazelle && git diff --exit-code + - name: Check that repositories.bzl is up-to-date + run: | + bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=repositories.bzl%go_repositories && + git diff --exit-code + - name: Check formatting of Bazel BUILD files + run: bazel run //:buildifier && git diff --exit-code + - name: Run tests with Bazel + run: bazel test //... + gorelease: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # tag=v3 + with: + go-version: 1.19 + check-latest: true + - run: go run golang.org/x/exp/cmd/gorelease@latest -base=v2.14.0 + proto_lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # tag=v3 + with: + check-latest: true + - run: make install + - run: PATH=$PATH:~/go/bin buf build + - run: PATH=$PATH:~/go/bin buf lint + - run: PATH=$PATH:~/go/bin buf format -w && git diff --exit-code + - run: PATH=$PATH:~/go/bin buf breaking --path protoc-gen-openapiv2/ --against 'https://github.com/grpc-ecosystem/grpc-gateway.git#branch=master' + golangci: + permissions: + pull-requests: read # for golangci/golangci-lint-action to fetch pull requests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - uses: golangci/golangci-lint-action@0ad9a0988b3973e851ab0a07adf248ec2e100376 # tag=v3 + with: + version: v1.45 + args: --enable goimports + fuzz: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: "grpc-gateway" + dry-run: false + language: go + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: "grpc-gateway" + fuzz-seconds: 600 + dry-run: false + language: go + - name: Upload Crash + uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 00000000000..dba8c4c6c99 --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,25 @@ +on: + push: + branches: + - master + paths: + - protoc-gen-openapiv2/options/*.proto +permissions: + contents: read +name: master +jobs: + proto_push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # tag=v3 + with: + check-latest: true + - run: make install + # Limit pushes to protoc-gen-openapiv2 files. This is a total hack. + # It excludes all the files that we don't want to publish, just for the push step. + - run: echo -e " - examples\n - internal\n - runtime" >> buf.yaml + - run: buf push --tag "${{ github.sha }}" + env: + BUF_TOKEN: ${{ secrets.BUF_TOKEN }} + PATH: $PATH:~/go/bin diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000000..a3c258b11fd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,96 @@ +on: + push: + tags: + - v2.[0-9]+.[0-9]+ + # For testing the workflow before pushing a tag + # This will run goreleaser with --snapshot and test the + # SLSA generator. + workflow_dispatch: +permissions: + contents: read +name: release +jobs: + goreleaser: + outputs: + hashes: ${{ steps.hash.outputs.hashes }} + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + with: + fetch-depth: 0 + - uses: actions/setup-go@c4a742cab115ed795e34d4513e2cf7d472deb55f # tag=v3 + with: + check-latest: true + - name: Generate goreleaser args + id: args + run: | + set -euo pipefail + args='release --rm-dist' + if [[ "$GITHUB_REF" != refs/tags/* ]]; then + args+=' --snapshot' + fi + echo "args=$args" >> $GITHUB_OUTPUT + - uses: goreleaser/goreleaser-action@b508e2e3ef3b19d4e4146d4f8fb3ba9db644a757 # tag=v3 + id: run-goreleaser + with: + args: ${{ steps.args.outputs.args }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Generate subject + id: hash + env: + ARTIFACTS: "${{ steps.run-goreleaser.outputs.artifacts }}" + run: | + set -euo pipefail + + checksum_file=$(echo "$ARTIFACTS" | jq -r '.[] | select (.type=="Checksum") | .path') + echo "::set-output name=hashes::$(cat $checksum_file | base64 -w0)" + provenance: + needs: [goreleaser] + permissions: + actions: read # To read the workflow path. + id-token: write # To sign the provenance. + contents: write # To add assets to a release. + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.3.0 + with: + compile-generator: true # Workaround for https://github.com/slsa-framework/slsa-github-generator/issues/1163 + base64-subjects: "${{ needs.goreleaser.outputs.hashes }}" + upload-assets: ${{ github.event_name == 'push' }} # upload to a new release when pushing via tag + + push_bsr_plugins: + runs-on: ubuntu-latest + if: github.event_name == 'push' + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + - uses: docker/setup-buildx-action@8c0edbc76e98fa90f69d9a2c020dcb50019dc325 # tag=v2 + - uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # tag=v2 + with: + registry: plugins.buf.build + username: grpcgatewaybot + password: ${{ secrets.BUF_TOKEN }} + - name: Set protobuf-version + run: echo ::set-output name=version::$(go list -m -f '{{.Version}}' google.golang.org/protobuf) + id: protobuf-version + - name: Set grpc-version + run: echo ::set-output name=version::$(go list -m -f '{{.Version}}' google.golang.org/grpc) + id: grpc-version + - uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 # tag=v3 + with: + push: true + tags: plugins.buf.build/grpc-ecosystem/grpc-gateway:${{ github.ref_name }}-1 + context: .github/plugins/protoc-gen-grpc-gateway + platforms: linux/amd64 + build-args: | + RELEASE_VERSION=${{ github.ref_name }} + GO_PROTOBUF_RELEASE_VERSION=${{ steps.protobuf-version.outputs.version }} + GO_GRPC_RELEASE_VERSION=${{ steps.grpc-version.outputs.version }} + - uses: docker/build-push-action@c56af957549030174b10d6867f20e78cfd7debc5 # tag=v3 + with: + push: true + tags: plugins.buf.build/grpc-ecosystem/openapiv2:${{ github.ref_name }}-1 + context: .github/plugins/protoc-gen-openapiv2 + platforms: linux/amd64 + build-args: | + RELEASE_VERSION=${{ github.ref_name }} diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml new file mode 100644 index 00000000000..625605b7f78 --- /dev/null +++ b/.github/workflows/renovate.yml @@ -0,0 +1,75 @@ +on: + push: + branches: + - renovate/* +permissions: + contents: read +name: renovate +jobs: + update_repositoriesbzl: + container: + image: docker.pkg.github.com/grpc-ecosystem/grpc-gateway/build-env:1.17 + options: "--user root" + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + with: + fetch-depth: 0 + token: ${{ secrets.GH_PUSH_TOKEN }} + - uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7 # tag=v3 + with: + path: /home/vscode/.cache/_grpc_gateway_bazel + key: v1-bazel-cache-${{ hashFiles('repositories.bzl') }} + restore-keys: v1-bazel-cache- + - name: Configure bazel + run: | + cat > .bazelrc << EOF + startup --output_base /home/vscode/.cache/_grpc_gateway_bazel + build --test_output errors + build --features race + # Workaround https://github.com/bazelbuild/bazel/issues/3645 + # See https://docs.bazel.build/versions/0.23.0/command-line-reference.html + build --local_ram_resources=7168 # Github runners have 7G of memory + build --local_cpu_resources=2 # Github runners have 2 vCPU + EOF + - run: bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=repositories.bzl%go_repositories + - run: | + git add . + if output=$(git status --porcelain) && [ ! -z "$output" ]; then + git config user.name "Renovate Bot" + git config user.email "bot@renovateapp.com" + git commit --amend --no-edit + git push --force-with-lease origin ${{ github.ref_name }} + fi + regenerate: + container: + image: docker.pkg.github.com/grpc-ecosystem/grpc-gateway/build-env:1.17 + options: "--user root" + credentials: + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + runs-on: ubuntu-latest + needs: + # Run after update_repositoriesbzl to avoid + # git conflicts + - update_repositoriesbzl + steps: + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3 + with: + fetch-depth: 0 + token: ${{ secrets.GH_PUSH_TOKEN }} + - run: make install + - run: make clean + - run: make generate + - run: go mod tidy + - run: | + git add . + if output=$(git status --porcelain) && [ ! -z "$output" ]; then + git config user.name "Renovate Bot" + git config user.email "bot@renovateapp.com" + git commit --amend --no-edit + git push --force-with-lease origin ${{ github.ref_name }} + fi diff --git a/.gitignore b/.gitignore index 55e4918abeb..74f5e82f1bf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,10 @@ bazel-genfiles bazel-grpc-gateway bazel-out bazel-testlogs +/.bazelrc + +# Go vendor directory +vendor + +# Generated travis files +.travis.yml diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 00000000000..aeb78fe380f --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,31 @@ +builds: + - main: ./protoc-gen-grpc-gateway/main.go + id: protoc-gen-grpc-gateway + binary: protoc-gen-grpc-gateway + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + - main: ./protoc-gen-openapiv2/main.go + id: protoc-gen-openapiv2 + binary: protoc-gen-openapiv2 + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 +archives: + - name_template: "{{ .Binary }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}" + format: binary + replacements: + amd64: x86_64 +dist: _output diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 323a464508b..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,81 +0,0 @@ -language: go -sudo: false -go: - - 1.9.x - - 1.10.x - - master -go_import_path: github.com/grpc-ecosystem/grpc-gateway -cache: - directories: - - $HOME/local - - ${TRAVIS_BUILD_DIR}/examples/browser/node_modules - - $HOME/.cache/_grpc_gateway_bazel -before_install: - - if [ "${USE_BAZEL}" = true ]; then ./.travis/install-bazel.sh $BAZEL_VERSION; fi - - test "${USE_BAZEL}" = true || ./.travis/install-protoc.sh $PROTOC_VERSION - - test "${USE_BAZEL}" = true || ./.travis/install-swagger-codegen.sh $SWAGGER_CODEGEN_VERSION - - test "${USE_BAZEL}" = true || (nvm install $NODE_VERSION && nvm use $NODE_VERSION && node --version) - - test "${USE_BAZEL}" = true || go get github.com/golang/lint/golint - - test "${USE_BAZEL}" = true || go get github.com/dghubble/sling - - test "${USE_BAZEL}" = true || go get github.com/go-resty/resty -install: - # Make sure externally referenced packages are go-gettable. - - test "${USE_BAZEL}" = true || - go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway - - test "${USE_BAZEL}" = true || - go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger - - test "${USE_BAZEL}" = true || - go get github.com/grpc-ecosystem/grpc-gateway/runtime - - test "${USE_BAZEL}" = true || - go get github.com/grpc-ecosystem/grpc-gateway/examples/cmd/example-grpc-server - - test "${USE_BAZEL}" = true || - go get github.com/grpc-ecosystem/grpc-gateway/examples/cmd/example-gateway-server - - # Just build if USE_BAZEL - - if [ "${USE_BAZEL}" = true ]; then ./.travis/bazel-build.sh; fi -before_script: - - test "${USE_BAZEL}" = true || - (cd examples/browser && npm install) -script: - # Make sure examples of generated files are up-to-date - - test "${USE_BAZEL}" = true || - (make realclean && make examples SWAGGER_CODEGEN="java -jar $HOME/local/swagger-codegen-cli.jar") - - if [ -z "${USE_BAZEL}" ] && - (go version | grep -q "${GO_VERSION_TO_DIFF_TEST}") && - [ -z "${GATEWAY_PLUGIN_FLAGS}" ]; then - test -z "$(git status --porcelain)" || (git status; git diff; exit 1); - fi - - # Unit tests, integration tests and code health checks - - test "${USE_BAZEL}" = true || - env GLOG_logtostderr=1 go test -race -v github.com/grpc-ecosystem/grpc-gateway/... - - test "${USE_BAZEL}" = true || - make lint - - test "${USE_BAZEL}" = true || - sh -c 'cd examples/browser && node ./node_modules/gulp/bin/gulp' - - - if [ "${USE_BAZEL}" = true ]; then ./.travis/bazel-test.sh; fi - - # test coverage - - if (go version | grep -q "${GO_VERSION_TO_DIFF_TEST}") && - [ -z "${GATEWAY_PLUGIN_FLAGS}" ]; then - env GLOG_logtostderr=1 ./bin/coverage; - fi -after_success: - - bash <(curl -s https://codecov.io/bash) - -env: - global: - - "PATH=$PATH:$HOME/local/bin" - - GO_VERSION_TO_DIFF_TEST="go version go1\.10\.[0-9]\+ linux/amd64" - - BAZEL_VERSION=0.12.0 - - NODE_VERSION=v6.1 - - PROTOC_VERSION=3.1.0 - - SWAGGER_CODEGEN_VERSION=2.2.2 - matrix: - - GATEWAY_PLUGIN_FLAGS= - - GATEWAY_PLUGIN_FLAGS=request_context=false -matrix: - include: - - go: master - env: USE_BAZEL=true diff --git a/.travis/bazel-build.sh b/.travis/bazel-build.sh deleted file mode 100755 index f46781d414b..00000000000 --- a/.travis/bazel-build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -eu - -bazel \ - --batch \ - --output_base=$HOME/.cache/_grpc_gateway_bazel \ - --host_jvm_args=-Xmx500m \ - --host_jvm_args=-Xms500m \ - build \ - --local_resources=400,1,1.0 \ - //... diff --git a/.travis/bazel-test.sh b/.travis/bazel-test.sh deleted file mode 100755 index 86dbbf3270a..00000000000 --- a/.travis/bazel-test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh -eu - -bazel \ - --batch \ - --output_base=$HOME/.cache/_grpc_gateway_bazel \ - --host_jvm_args=-Xmx500m \ - --host_jvm_args=-Xms500m \ - test \ - --local_resources=400,1,1.0 \ - --test_output=errors \ - --features=race \ - //... diff --git a/.travis/install-bazel.sh b/.travis/install-bazel.sh deleted file mode 100755 index 9bebe44cdd2..00000000000 --- a/.travis/install-bazel.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh -eu - -bazel_version=$1 - -if test -z "${bazel_version}"; then - echo "Usage: .travis/install-bazel.sh bazel-version" - exit 1 -fi - -if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then - OS=darwin -else - OS=linux -fi - -filename=bazel-${bazel_version}-installer-${OS}-x86_64.sh -wget https://github.com/bazelbuild/bazel/releases/download/${bazel_version}/${filename} -chmod +x $filename -./$filename --user -rm -f $filename diff --git a/.travis/install-protoc.sh b/.travis/install-protoc.sh deleted file mode 100755 index d96c259272a..00000000000 --- a/.travis/install-protoc.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -eu -protoc_version=$1 -if test -z "${protoc_version}"; then - echo "Usage: .travis/install-protoc.sh protoc-version" - exit 1 -fi -if [ "`$HOME/local/bin/protoc-${protoc_version} --version 2>/dev/null | cut -d' ' -f 2`" != ${protoc_version} ]; then - rm -rf $HOME/local/bin $HOME/local/include - - mkdir -p $HOME/tmp $HOME/local - cd $HOME/tmp - wget https://github.com/google/protobuf/releases/download/v${protoc_version}/protoc-${protoc_version}-linux-x86_64.zip - unzip protoc-${protoc_version}-linux-x86_64.zip - mv bin $HOME/local/bin - mv include $HOME/local/include -fi - -echo \$ $HOME/local/bin/protoc --version -$HOME/local/bin/protoc --version diff --git a/.travis/install-swagger-codegen.sh b/.travis/install-swagger-codegen.sh deleted file mode 100755 index ad4c8b56815..00000000000 --- a/.travis/install-swagger-codegen.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -eu -codegen_version=$1 -if test -z "${codegen_version}"; then - echo "Usage: .travis/install-swagger-codegen.sh codegen-version" - exit 1 -fi - -wget http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/${codegen_version}/swagger-codegen-cli-${codegen_version}.jar \ - -O $HOME/local/swagger-codegen-cli.jar diff --git a/ADOPTERS.md b/ADOPTERS.md new file mode 100644 index 00000000000..dafe8d29fdb --- /dev/null +++ b/ADOPTERS.md @@ -0,0 +1,24 @@ +# Adopters + +This is a list of organizations that have spoken publicly about their adoption or +production users that have added themselves (in alphabetical order): + +- [Ad Hoc](http://adhocteam.us/) uses the gRPC-Gateway to serve millions of + API requests per day. +- [Chef](https://www.chef.io/) uses the gRPC-Gateway to provide the user-facing + API of [Chef Automate](https://automate.chef.io/). Furthermore, the generated + OpenAPI data serves as the basis for its [API documentation](https://automate.chef.io/docs/api/). + The code is Open Source, [see `github.com/chef/automate`](https://github.com/chef/automate). +- [Conduit](https://github.com/ConduitIO/conduit), a data streaming tool written in Go, + uses the gRPC-Gateway since its very beginning to provide an HTTP API in addition to its gRPC API. + This makes it easier to integrate with Conduit, and the generated OpenAPI data is used in the documentation. +- [Scaleway](https://www.scaleway.com/en/) uses the gRPC-Gateway since 2018 to + serve millions of API requests per day [1]. +- [SpiceDB](https://github.com/authzed/spicedb) uses the gRPC-Gateway to handle + requests for security-critical permissions checks in environments where gRPC + is unavailable. + +If you have adopted the gRPC-Gateway and would like to be included in this list, +feel free to submit a PR. + +[1]: [The odyssey of an HTTP request in Scaleway](https://www.youtube.com/watch?v=eLxD-zIUraE&feature=youtu.be&t=480). diff --git a/BUILD b/BUILD index 0ab0afa13f7..90805c1af99 100644 --- a/BUILD +++ b/BUILD @@ -1,23 +1,58 @@ load("@bazel_gazelle//:def.bzl", "gazelle") +load("@com_github_bazelbuild_buildtools//buildifier:def.bzl", "buildifier") +load("@io_bazel_rules_go//proto:compiler.bzl", "go_proto_compiler") +load("@io_bazel_rules_go//proto/wkt:well_known_types.bzl", "PROTO_RUNTIME_DEPS", "WELL_KNOWN_TYPES_APIV2") -# gazelle:exclude third_party +exports_files(["LICENSE.txt"]) -gazelle( - name = "gazelle_diff", - mode = "diff", - prefix = "github.com/grpc-ecosystem/grpc-gateway", +buildifier( + name = "buildifier", ) -gazelle( - name = "gazelle_fix", - mode = "fix", - prefix = "github.com/grpc-ecosystem/grpc-gateway", +buildifier( + name = "buildifier_check", + mode = "check", ) +# gazelle:exclude _output +# gazelle:prefix github.com/grpc-ecosystem/grpc-gateway/v2 +# gazelle:go_proto_compilers //:go_apiv2 +# gazelle:go_grpc_compilers //:go_apiv2, //:go_grpc +# gazelle:go_naming_convention import_alias + +gazelle(name = "gazelle") + package_group( name = "generators", packages = [ "//protoc-gen-grpc-gateway/...", - "//protoc-gen-swagger/...", + "//protoc-gen-openapiv2/...", + ], +) + +go_proto_compiler( + name = "go_apiv2", + options = [ + "paths=source_relative", + ], + plugin = "@org_golang_google_protobuf//cmd/protoc-gen-go", + suffix = ".pb.go", + visibility = ["//visibility:public"], + deps = PROTO_RUNTIME_DEPS + WELL_KNOWN_TYPES_APIV2, +) + +go_proto_compiler( + name = "go_grpc", + options = [ + "paths=source_relative", + "require_unimplemented_servers=false", + ], + plugin = "@org_golang_google_grpc_cmd_protoc_gen_go_grpc//:protoc-gen-go-grpc", + suffix = "_grpc.pb.go", + visibility = ["//visibility:public"], + deps = PROTO_RUNTIME_DEPS + [ + "@org_golang_google_grpc//:go_default_library", + "@org_golang_google_grpc//codes:go_default_library", + "@org_golang_google_grpc//status:go_default_library", ], ) diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 2855894912e..00000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,215 +0,0 @@ -# Change Log - -## [1.3.1](https://github.com/grpc-ecosystem/grpc-gateway/tree/1.3.1) (2017-12-23) -**Merged pull requests:** - -- Add support for --Import\_path [\#507](https://github.com/grpc-ecosystem/grpc-gateway/pull/507) -- Fix \#504 Missing Definitions [\#505](https://github.com/grpc-ecosystem/grpc-gateway/pull/505) -- Maintain default delimiter of newline [\#497](https://github.com/grpc-ecosystem/grpc-gateway/pull/497) -- Fix gen-swagger to support more well known types [\#496](https://github.com/grpc-ecosystem/grpc-gateway/pull/496) -- Use golang/protobuf instead of gogo/protobuf [\#494](https://github.com/grpc-ecosystem/grpc-gateway/pull/494) -- Fix stream delimiters [\#488](https://github.com/grpc-ecosystem/grpc-gateway/pull/488) -- ForwardResponseStream status code errors [\#482](https://github.com/grpc-ecosystem/grpc-gateway/pull/482) -- protoc-gen-grpc-gateway: flip request\_context default to true [\#474](https://github.com/grpc-ecosystem/grpc-gateway/pull/474) -- grpc-gateway/generator: respect full package [\#462](https://github.com/grpc-ecosystem/grpc-gateway/pull/462) -- Add proto marshaller for proto-over-http [\#459](https://github.com/grpc-ecosystem/grpc-gateway/pull/459) - -## [v1.3](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.3) (2017-11-03) -## [v1.3.0](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.3.0) (2017-11-03) -**Merged pull requests:** - -- Streaming forward handler fix chunk encoding [\#479](https://github.com/grpc-ecosystem/grpc-gateway/pull/479) -- Fix logic handling primitive wrapper in URL params [\#478](https://github.com/grpc-ecosystem/grpc-gateway/pull/478) -- runtime: use r.Context\(\) [\#473](https://github.com/grpc-ecosystem/grpc-gateway/pull/473) -- Optional SourceCodeInfo [\#466](https://github.com/grpc-ecosystem/grpc-gateway/pull/466) -- Some steps to fix Travis CI [\#461](https://github.com/grpc-ecosystem/grpc-gateway/pull/461) -- fix 2 typos in Registry.SetPrefix's comment [\#455](https://github.com/grpc-ecosystem/grpc-gateway/pull/455) -- Add Handler method to pass in client [\#454](https://github.com/grpc-ecosystem/grpc-gateway/pull/454) -- Fallback to JSON name when matching URL parameter. [\#450](https://github.com/grpc-ecosystem/grpc-gateway/pull/450) -- Update DO NOT EDIT template. [\#434](https://github.com/grpc-ecosystem/grpc-gateway/pull/434) -- Memoise calls to fullyQualifiedNameToSwaggerName to speed it up for large registries [\#421](https://github.com/grpc-ecosystem/grpc-gateway/pull/421) -- Update Swagger Codegen from 2.1.6 to 2.2.2 [\#415](https://github.com/grpc-ecosystem/grpc-gateway/pull/415) -- Return codes.InvalidArgument to rather return HTTP 400 instead of HTTP 500 [\#409](https://github.com/grpc-ecosystem/grpc-gateway/pull/409) -- improve {incoming,outgoing}HeaderMatcher logic [\#408](https://github.com/grpc-ecosystem/grpc-gateway/pull/408) -- improve WKT handling in gateway and openapi output [\#404](https://github.com/grpc-ecosystem/grpc-gateway/pull/404) -- Return if runtime.AnnotateContext gave error [\#403](https://github.com/grpc-ecosystem/grpc-gateway/pull/403) -- jsonpb: update tests to reflect new jsonpb behavior [\#401](https://github.com/grpc-ecosystem/grpc-gateway/pull/401) -- Reference import grpc Status to suppress unused errors. [\#387](https://github.com/grpc-ecosystem/grpc-gateway/pull/387) -- ci: regen with current protoc-gen-go [\#385](https://github.com/grpc-ecosystem/grpc-gateway/pull/385) -- Use status package for error and introduce WithProtoErrorHandler option [\#378](https://github.com/grpc-ecosystem/grpc-gateway/pull/378) -- Return response headers from grpc server [\#374](https://github.com/grpc-ecosystem/grpc-gateway/pull/374) -- Skip unreferenced messages in definitions. [\#371](https://github.com/grpc-ecosystem/grpc-gateway/pull/371) -- Use canonical header form in default header matcher. [\#369](https://github.com/grpc-ecosystem/grpc-gateway/pull/369) -- support allow\_delete\_body for protoc-gen-grpc-gateway [\#318](https://github.com/grpc-ecosystem/grpc-gateway/pull/318) -- fixes package name override doesn't work [\#277](https://github.com/grpc-ecosystem/grpc-gateway/pull/277) -- add custom options to allow more control of swagger/openapi output [\#145](https://github.com/grpc-ecosystem/grpc-gateway/pull/145) - -## [v1.2.2](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.2.2) (2017-04-17) -**Merged pull requests:** - -- Add changelog for 1.2.2 [\#363](https://github.com/grpc-ecosystem/grpc-gateway/pull/363) -- metadata: fix properly and change to Outgoing [\#361](https://github.com/grpc-ecosystem/grpc-gateway/pull/361) - -## [v1.2.1](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.2.1) (2017-04-17) -**Merged pull requests:** - -- Add changelog for 1.2.1 [\#360](https://github.com/grpc-ecosystem/grpc-gateway/pull/360) -- bugfix: reflect upstream api change. [\#359](https://github.com/grpc-ecosystem/grpc-gateway/pull/359) - -## [v1.2.0](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.2.0) (2017-03-31) -**Merged pull requests:** - -- Add changelog for 1.2.0 [\#342](https://github.com/grpc-ecosystem/grpc-gateway/pull/342) - -## [v1.2.0.rc1](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.2.0.rc1) (2017-03-24) -**Merged pull requests:** - -- Support user configurable header forwarding & context metadata [\#336](https://github.com/grpc-ecosystem/grpc-gateway/pull/336) -- Update go\_out parameter to remove comma [\#333](https://github.com/grpc-ecosystem/grpc-gateway/pull/333) -- Update stale path in README [\#332](https://github.com/grpc-ecosystem/grpc-gateway/pull/332) -- improve documentation regarding external dependencies [\#330](https://github.com/grpc-ecosystem/grpc-gateway/pull/330) -- Return an error on invalid nested query parameters. [\#329](https://github.com/grpc-ecosystem/grpc-gateway/pull/329) -- Update upstream proto files and add google.golang.org/genproto support. [\#325](https://github.com/grpc-ecosystem/grpc-gateway/pull/325) -- Support oneof fields in query params [\#321](https://github.com/grpc-ecosystem/grpc-gateway/pull/321) -- Do not ignore the error coming from http.ListenAndServe in examples [\#319](https://github.com/grpc-ecosystem/grpc-gateway/pull/319) -- Look up enum value maps by their proto name [\#315](https://github.com/grpc-ecosystem/grpc-gateway/pull/315) -- enable parsing enums from query parameters [\#314](https://github.com/grpc-ecosystem/grpc-gateway/pull/314) -- Do not add imports from methods with no bindings. [\#312](https://github.com/grpc-ecosystem/grpc-gateway/pull/312) -- Convert the first letter of method name to upper [\#300](https://github.com/grpc-ecosystem/grpc-gateway/pull/300) -- write query parameters to swagger definition [\#297](https://github.com/grpc-ecosystem/grpc-gateway/pull/297) -- Bump swagger-client to 2.1.28 for examples/browser [\#290](https://github.com/grpc-ecosystem/grpc-gateway/pull/290) -- pin to version before es6ism [\#289](https://github.com/grpc-ecosystem/grpc-gateway/pull/289) -- Prevent lack of http bindings from generating non-building output [\#286](https://github.com/grpc-ecosystem/grpc-gateway/pull/286) -- Added support for Timestamp in URL. [\#281](https://github.com/grpc-ecosystem/grpc-gateway/pull/281) -- add plugin param 'allow\_delete\_body' [\#280](https://github.com/grpc-ecosystem/grpc-gateway/pull/280) -- Fix ruby gen command [\#275](https://github.com/grpc-ecosystem/grpc-gateway/pull/275) -- Make grpc-gateway support enum fields in path parameter [\#273](https://github.com/grpc-ecosystem/grpc-gateway/pull/273) -- remove unnecessary make\(\) [\#271](https://github.com/grpc-ecosystem/grpc-gateway/pull/271) -- preserve field order in swagger spec [\#270](https://github.com/grpc-ecosystem/grpc-gateway/pull/270) -- Merge \#228 [\#268](https://github.com/grpc-ecosystem/grpc-gateway/pull/268) -- Handle methods with no bindings more carefully [\#267](https://github.com/grpc-ecosystem/grpc-gateway/pull/267) -- describe default marshaler in README.md [\#266](https://github.com/grpc-ecosystem/grpc-gateway/pull/266) -- Add request\_context flag to utilize \(\*http.Request\).Context\(\) in handlers [\#265](https://github.com/grpc-ecosystem/grpc-gateway/pull/265) -- Regenerate examples [\#264](https://github.com/grpc-ecosystem/grpc-gateway/pull/264) -- Correct runtime.errorBody protobuf field tag [\#256](https://github.com/grpc-ecosystem/grpc-gateway/pull/256) -- Pass permanent HTTP request headers [\#252](https://github.com/grpc-ecosystem/grpc-gateway/pull/252) -- regenerate examples, fix tests for go tip [\#248](https://github.com/grpc-ecosystem/grpc-gateway/pull/248) -- Render the swagger request body properly [\#247](https://github.com/grpc-ecosystem/grpc-gateway/pull/247) -- Error output should have lowercase attribute names [\#244](https://github.com/grpc-ecosystem/grpc-gateway/pull/244) -- runtime - export prefix constants [\#236](https://github.com/grpc-ecosystem/grpc-gateway/pull/236) -- README - Add CoreOS example [\#231](https://github.com/grpc-ecosystem/grpc-gateway/pull/231) -- Docs - Add section about how HTTP maps to gRPC [\#227](https://github.com/grpc-ecosystem/grpc-gateway/pull/227) -- readme: added links to additional documentation [\#222](https://github.com/grpc-ecosystem/grpc-gateway/pull/222) -- Use a released version of protoc [\#216](https://github.com/grpc-ecosystem/grpc-gateway/pull/216) -- Add contribution guideline [\#210](https://github.com/grpc-ecosystem/grpc-gateway/pull/210) -- improve\(genswagger:template\):added support for google.protobuf.Timestamp [\#209](https://github.com/grpc-ecosystem/grpc-gateway/pull/209) -- Allowing unknown fields to be dropped instead of returning error from… [\#208](https://github.com/grpc-ecosystem/grpc-gateway/pull/208) -- Avoid Internal Server Error on zero-length input for bidi streaming [\#200](https://github.com/grpc-ecosystem/grpc-gateway/pull/200) - -## [v1.1.0](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.1.0) (2016-07-23) -**Merged pull requests:** - -- Rename packages to follow the repository transfer [\#192](https://github.com/grpc-ecosystem/grpc-gateway/pull/192) -- return err early if EOF to prevent logging in normal conditions [\#191](https://github.com/grpc-ecosystem/grpc-gateway/pull/191) -- send Trailer header on error [\#188](https://github.com/grpc-ecosystem/grpc-gateway/pull/188) -- generate swagger output for streaming endpoints with a basic note [\#183](https://github.com/grpc-ecosystem/grpc-gateway/pull/183) - -## [v1.0.0](https://github.com/grpc-ecosystem/grpc-gateway/tree/v1.0.0) (2016-06-15) -**Merged pull requests:** - -- Regenerate files with the latest protoc-gen-go [\#185](https://github.com/grpc-ecosystem/grpc-gateway/pull/185) -- Add browser examples [\#184](https://github.com/grpc-ecosystem/grpc-gateway/pull/184) -- Fix golint and go vet errors [\#182](https://github.com/grpc-ecosystem/grpc-gateway/pull/182) -- Add integration with clients generated by swagger-codegen [\#181](https://github.com/grpc-ecosystem/grpc-gateway/pull/181) -- Simplify example services [\#180](https://github.com/grpc-ecosystem/grpc-gateway/pull/180) -- Avoid errors when req.RemoteAddr is empty [\#178](https://github.com/grpc-ecosystem/grpc-gateway/pull/178) -- Feature/headers [\#176](https://github.com/grpc-ecosystem/grpc-gateway/pull/176) -- Include HTTP req.remoteAddr in gRPC ctx [\#174](https://github.com/grpc-ecosystem/grpc-gateway/pull/174) -- Update dependencies [\#171](https://github.com/grpc-ecosystem/grpc-gateway/pull/171) -- Add bidirectional streaming support by running Send\(\) and Recv\(\) concurrently [\#170](https://github.com/grpc-ecosystem/grpc-gateway/pull/170) -- make Authorization header check case-insensitive to comply with RFC 2616 4.2 [\#164](https://github.com/grpc-ecosystem/grpc-gateway/pull/164) -- jsonpb: avoid duplicating upstream's struct [\#158](https://github.com/grpc-ecosystem/grpc-gateway/pull/158) -- Generate Swagger description for service methods using proto comments. [\#156](https://github.com/grpc-ecosystem/grpc-gateway/pull/156) -- Implement gRPC timeout support for inbound HTTP headers [\#155](https://github.com/grpc-ecosystem/grpc-gateway/pull/155) -- Add more examples to marshalers [\#154](https://github.com/grpc-ecosystem/grpc-gateway/pull/154) -- custom marshaler: handle `Accept` headers correctly [\#152](https://github.com/grpc-ecosystem/grpc-gateway/pull/152) -- Simplify custom marshaler API [\#151](https://github.com/grpc-ecosystem/grpc-gateway/pull/151) -- Fix camel case path parameter handling in swagger [\#149](https://github.com/grpc-ecosystem/grpc-gateway/pull/149) -- Swagger dot in path template [\#148](https://github.com/grpc-ecosystem/grpc-gateway/pull/148) -- Support map types in swagger generator [\#147](https://github.com/grpc-ecosystem/grpc-gateway/pull/147) -- Cleanup custom marshaler [\#146](https://github.com/grpc-ecosystem/grpc-gateway/pull/146) -- Implement custom Marshaler support, add jsonpb implemention. [\#144](https://github.com/grpc-ecosystem/grpc-gateway/pull/144) -- Allow period in path URL templates when generating Swagger templates. [\#143](https://github.com/grpc-ecosystem/grpc-gateway/pull/143) -- Link to LICENSE.txt [\#142](https://github.com/grpc-ecosystem/grpc-gateway/pull/142) -- Support map types in swagger generator [\#141](https://github.com/grpc-ecosystem/grpc-gateway/pull/141) -- Conditionally stops checking if generated file are up-to-date [\#136](https://github.com/grpc-ecosystem/grpc-gateway/pull/136) -- Generate Swagger description for service methods using proto comments. [\#134](https://github.com/grpc-ecosystem/grpc-gateway/pull/134) -- Swagger definitions now have `type` set to `object`. [\#133](https://github.com/grpc-ecosystem/grpc-gateway/pull/133) -- go\_package option as go import path [\#129](https://github.com/grpc-ecosystem/grpc-gateway/pull/129) -- Fix govet errors [\#126](https://github.com/grpc-ecosystem/grpc-gateway/pull/126) -- Fix data-race in generated codes [\#125](https://github.com/grpc-ecosystem/grpc-gateway/pull/125) -- Fix \#119 - CloseNotify race with ServeHTTP [\#120](https://github.com/grpc-ecosystem/grpc-gateway/pull/120) -- Replace glog with grpclog [\#118](https://github.com/grpc-ecosystem/grpc-gateway/pull/118) -- Fix a goroutine-leak in HTTP keep-alive [\#116](https://github.com/grpc-ecosystem/grpc-gateway/pull/116) -- Fix camel case path parameter handling in swagger [\#114](https://github.com/grpc-ecosystem/grpc-gateway/pull/114) -- gofmt -s [\#112](https://github.com/grpc-ecosystem/grpc-gateway/pull/112) -- fix typo [\#111](https://github.com/grpc-ecosystem/grpc-gateway/pull/111) -- fix typo [\#110](https://github.com/grpc-ecosystem/grpc-gateway/pull/110) -- fixes missing swagger operation objects [\#109](https://github.com/grpc-ecosystem/grpc-gateway/pull/109) -- Add parser and swagger support for enum, no gengateway yet [\#108](https://github.com/grpc-ecosystem/grpc-gateway/pull/108) -- README: add protoc-gen-swagger too [\#105](https://github.com/grpc-ecosystem/grpc-gateway/pull/105) -- README: Suggest go get -u by default. [\#104](https://github.com/grpc-ecosystem/grpc-gateway/pull/104) -- Cancel context when HTTP connection is closed [\#102](https://github.com/grpc-ecosystem/grpc-gateway/pull/102) -- wait test server up [\#100](https://github.com/grpc-ecosystem/grpc-gateway/pull/100) -- Fix the swagger section of the README.md [\#98](https://github.com/grpc-ecosystem/grpc-gateway/pull/98) -- Add documentation for using Swagger [\#97](https://github.com/grpc-ecosystem/grpc-gateway/pull/97) -- Better compatibility to field names generated by protoc-gen-go [\#96](https://github.com/grpc-ecosystem/grpc-gateway/pull/96) -- Update protoc from 3.0.0-beta1 to 3.0.0-beta2 [\#95](https://github.com/grpc-ecosystem/grpc-gateway/pull/95) -- Better grpc error strings [\#94](https://github.com/grpc-ecosystem/grpc-gateway/pull/94) -- make available header and trailer metadata [\#93](https://github.com/grpc-ecosystem/grpc-gateway/pull/93) -- make grpc.DialOption configurable [\#89](https://github.com/grpc-ecosystem/grpc-gateway/pull/89) -- Add request in error handlers [\#88](https://github.com/grpc-ecosystem/grpc-gateway/pull/88) -- Improve PascalFromSnake behavior [\#85](https://github.com/grpc-ecosystem/grpc-gateway/pull/85) -- Typo grcp -\> grpc [\#81](https://github.com/grpc-ecosystem/grpc-gateway/pull/81) -- Add abstraction of code generator implementation [\#78](https://github.com/grpc-ecosystem/grpc-gateway/pull/78) -- Support multivalue of metadata [\#77](https://github.com/grpc-ecosystem/grpc-gateway/pull/77) -- Fix broken test [\#76](https://github.com/grpc-ecosystem/grpc-gateway/pull/76) -- Added missing instruction line in README [\#75](https://github.com/grpc-ecosystem/grpc-gateway/pull/75) -- Fix a complie error in generated go files [\#71](https://github.com/grpc-ecosystem/grpc-gateway/pull/71) -- Update generated .pb.go files in third\_party [\#69](https://github.com/grpc-ecosystem/grpc-gateway/pull/69) -- Add swagger support [\#68](https://github.com/grpc-ecosystem/grpc-gateway/pull/68) -- Bugfix/handling headers for `Authorization` and `Host` [\#65](https://github.com/grpc-ecosystem/grpc-gateway/pull/65) -- Fix `error` field always in chunk response [\#64](https://github.com/grpc-ecosystem/grpc-gateway/pull/64) -- Update .pb.go to latest version. [\#63](https://github.com/grpc-ecosystem/grpc-gateway/pull/63) -- Run more tests in Travis CI [\#60](https://github.com/grpc-ecosystem/grpc-gateway/pull/60) -- Added http error code and error status for responseStreamChunk error [\#59](https://github.com/grpc-ecosystem/grpc-gateway/pull/59) -- Fix parsing of verb and final path component. [\#55](https://github.com/grpc-ecosystem/grpc-gateway/pull/55) -- Add runtime.WithForwardResponseOption [\#53](https://github.com/grpc-ecosystem/grpc-gateway/pull/53) -- add grpc.WithInsecure\(\) as option for grpc.Dial call in template [\#52](https://github.com/grpc-ecosystem/grpc-gateway/pull/52) -- update .pb.go files for latest golang proto generation [\#51](https://github.com/grpc-ecosystem/grpc-gateway/pull/51) -- Fix a build error with the latest protoc-gen-go [\#50](https://github.com/grpc-ecosystem/grpc-gateway/pull/50) -- Configure Travis CI [\#49](https://github.com/grpc-ecosystem/grpc-gateway/pull/49) -- Follow a change of go package name convention in protoc-gen-go [\#48](https://github.com/grpc-ecosystem/grpc-gateway/pull/48) -- Consider tail segments after deep wildcard [\#47](https://github.com/grpc-ecosystem/grpc-gateway/pull/47) -- Fix typo in README [\#45](https://github.com/grpc-ecosystem/grpc-gateway/pull/45) -- Fix undefined variable error in generated codes [\#42](https://github.com/grpc-ecosystem/grpc-gateway/pull/42) -- Follow changes in protoc-gen-go and grpc-go [\#41](https://github.com/grpc-ecosystem/grpc-gateway/pull/41) -- Fixes \#4 [\#40](https://github.com/grpc-ecosystem/grpc-gateway/pull/40) -- fix examples to work with go1.5 [\#39](https://github.com/grpc-ecosystem/grpc-gateway/pull/39) -- rename internal to utilties for 1.5 compatibility [\#38](https://github.com/grpc-ecosystem/grpc-gateway/pull/38) -- Reflection fix of proto3 nested messages. [\#34](https://github.com/grpc-ecosystem/grpc-gateway/pull/34) -- \[Experimental\] Make the response forwarder function customizable [\#31](https://github.com/grpc-ecosystem/grpc-gateway/pull/31) -- Add f.Flush\(\) to runtime.ForwardResponseStream [\#30](https://github.com/grpc-ecosystem/grpc-gateway/pull/30) -- Format error message in JSON [\#29](https://github.com/grpc-ecosystem/grpc-gateway/pull/29) -- Update examples with HTTP header context annotation [\#28](https://github.com/grpc-ecosystem/grpc-gateway/pull/28) -- Report semantic errors in the source to protoc [\#27](https://github.com/grpc-ecosystem/grpc-gateway/pull/27) -- Add support for non-nullable nested messages. [\#21](https://github.com/grpc-ecosystem/grpc-gateway/pull/21) -- Receive GRPC metadata from HTTP headers. [\#18](https://github.com/grpc-ecosystem/grpc-gateway/pull/18) -- Implement detailed specs of google.api.http [\#14](https://github.com/grpc-ecosystem/grpc-gateway/pull/14) -- Configure travis CI [\#13](https://github.com/grpc-ecosystem/grpc-gateway/pull/13) -- Replace our own custom option with the one defined by Google [\#12](https://github.com/grpc-ecosystem/grpc-gateway/pull/12) -- Remove useless context setup [\#11](https://github.com/grpc-ecosystem/grpc-gateway/pull/11) -- Fix typo, path, missing semicolon. [\#10](https://github.com/grpc-ecosystem/grpc-gateway/pull/10) -- Use a globally unique id for the custom option [\#3](https://github.com/grpc-ecosystem/grpc-gateway/pull/3) -- implement ABitOfEverythingService [\#2](https://github.com/grpc-ecosystem/grpc-gateway/pull/2) -- support streaming API calls [\#1](https://github.com/grpc-ecosystem/grpc-gateway/pull/1) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4f0c59eea8d..c6c8cd6dbed 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,20 +1,63 @@ # How to contribute -Thank you for your contribution to grpc-gateway. -Here's the recommended process of contribution. - -1. `go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway` -2. `cd $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway` -3. hack, hack, hack... -4. Make sure that your change follows best practices in Go - * [Effective Go](https://golang.org/doc/effective_go.html) - * [Go Code Review Comments](https://golang.org/wiki/CodeReviewComments) -5. Make sure that `make test` passes. (use swagger-codegen 2.2.2, not newer versions) -6. Sign [a Contributor License Agreement](https://cla.developers.google.com/clas) -7. Open a pull request in Github - -When you work on a larger contribution, it is also recommended that you get in touch -with us through the issue tracker. - -### Code reviews +## Code reviews + All submissions, including submissions by project members, require review. + +## I want to regenerate the files after making changes + +### Using Docker + +It should be as simple as this (run from the root of the repository): + +```bash +docker run -v $(pwd):/grpc-gateway -w /grpc-gateway --rm ghcr.io/grpc-ecosystem/grpc-gateway/build-env:1.17 \ + /bin/bash -c 'make install && \ + make clean && \ + make generate' +docker run -itv $(pwd):/grpc-gateway -w /grpc-gateway --entrypoint /bin/bash --rm \ + ghcr.io/grpc-ecosystem/grpc-gateway/build-env:1.17 -c '\ + bazel run :gazelle -- update-repos -from_file=go.mod -to_macro=repositories.bzl%go_repositories && \ + bazel run :gazelle && \ + bazel run :buildifier' +``` + +You may need to authenticate with GitHub to pull `ghcr.io/grpc-ecosystem/grpc-gateway/build-env`. +You can do this by following the steps on the [GitHub Package docs](https://help.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages#authenticating-to-github-packages). + +### Using Visual Studio Code dev containers + +This repo contains a `devcontainer.json` configuration that sets up the build environment in a container using +[VS Code dev containers](https://code.visualstudio.com/docs/remote/containers). If you're using the dev container, +you can run the commands directly in your terminal: + +```sh +$ make install && make clean && make generate +``` + +```sh +$ bazel run :gazelle -- update-repos -from_file=go.mod -to_macro=repositories.bzl%go_repositories && \ + bazel run :gazelle && \ + bazel run :buildifier +``` + +Note that the above-listed docker commands will not work in the dev container, since volume mounts from +nested docker container is not possible. + +If this has resulted in some file changes in the repo, please ensure you check those in with your merge request. + +## Making a release + +To make a release, follow these steps: + +1. Decide on a release version. The `gorelease` job can + recommend whether the new release should be a patch or minor release. +1. Tag the release on `master`. + 1. The release can be created using the command line, or also through GitHub's [releases + UI](https://github.com/grpc-ecosystem/grpc-gateway/releases/new). + 1. If you create a release using the web UI you can publish it as a draft and have it + reviewed by another maintainer. + 1. Update the release description. Try to include some of the highlights of this release, + ideally with links to the PRs and crediting the contributors. +1. Update the gorelease job in .github/ci.yaml to point to the new release version. +1. Sit back and pat yourself on the back for a job well done :clap:. diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md deleted file mode 100644 index 9edbaa64c96..00000000000 --- a/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,41 +0,0 @@ - -# Please follow the general troubleshooting steps first: - -- [ ] Update your protoc to the [latest version](https://github.com/google/protobuf/releases) -- [ ] Update your copy of `grpc-gateway` to the latest version from github. with - `git fetch https://github.com/grpc-ecosystem/grpc-gateway master && git reset --hard FETCH_HEAD` -- [ ] Delete the `protoc-gen-grpc-gateway` and `protoc-gen-swagger` binary from your `PATH`, - and install locally built binaries. - -### Bug reports: - -Fill in the following sections with explanations of what's gone wrong. - -Steps you follow to reproduce the error: - - - -Your steps here. - -What did you expect to happen instead: - - - -Your answer here. - -What's your theory on why it isn't working: - - - -Your theory here. diff --git a/Makefile b/Makefile index 890f7ede3fd..0570aa11cc4 100644 --- a/Makefile +++ b/Makefile @@ -1,193 +1,147 @@ # This is a Makefile which maintains files automatically generated but to be # shipped together with other files. # You don't have to rebuild these targets by yourself unless you develop -# grpc-gateway itself. +# gRPC-Gateway itself. -PKG=github.com/grpc-ecosystem/grpc-gateway -GO_PLUGIN=bin/protoc-gen-go -GO_PROTOBUF_REPO=github.com/golang/protobuf -GO_PLUGIN_PKG=$(GO_PROTOBUF_REPO)/protoc-gen-go -GO_PTYPES_ANY_PKG=$(GO_PROTOBUF_REPO)/ptypes/any -SWAGGER_PLUGIN=bin/protoc-gen-swagger -SWAGGER_PLUGIN_SRC= utilities/doc.go \ - utilities/pattern.go \ - utilities/trie.go \ - protoc-gen-swagger/genswagger/generator.go \ - protoc-gen-swagger/genswagger/template.go \ - protoc-gen-swagger/main.go -SWAGGER_PLUGIN_PKG=$(PKG)/protoc-gen-swagger -GATEWAY_PLUGIN=bin/protoc-gen-grpc-gateway -GATEWAY_PLUGIN_PKG=$(PKG)/protoc-gen-grpc-gateway -GATEWAY_PLUGIN_SRC= utilities/doc.go \ - utilities/pattern.go \ - utilities/trie.go \ - protoc-gen-grpc-gateway \ - protoc-gen-grpc-gateway/descriptor \ - protoc-gen-grpc-gateway/descriptor/registry.go \ - protoc-gen-grpc-gateway/descriptor/services.go \ - protoc-gen-grpc-gateway/descriptor/types.go \ - protoc-gen-grpc-gateway/descriptor/grpc_api_configuration.go \ - protoc-gen-grpc-gateway/descriptor/grpc_api_service.go \ - protoc-gen-grpc-gateway/generator \ - protoc-gen-grpc-gateway/generator/generator.go \ - protoc-gen-grpc-gateway/gengateway \ - protoc-gen-grpc-gateway/gengateway/doc.go \ - protoc-gen-grpc-gateway/gengateway/generator.go \ - protoc-gen-grpc-gateway/gengateway/template.go \ - protoc-gen-grpc-gateway/httprule \ - protoc-gen-grpc-gateway/httprule/compile.go \ - protoc-gen-grpc-gateway/httprule/parse.go \ - protoc-gen-grpc-gateway/httprule/types.go \ - protoc-gen-grpc-gateway/main.go -GATEWAY_PLUGIN_FLAGS?= -SWAGGER_PLUGIN_FLAGS?= - -GOOGLEAPIS_DIR=third_party/googleapis -OUTPUT_DIR=_output - -RUNTIME_PROTO=runtime/internal/stream_chunk.proto -RUNTIME_GO=$(RUNTIME_PROTO:.proto=.pb.go) - -OPENAPIV2_PROTO=protoc-gen-swagger/options/openapiv2.proto protoc-gen-swagger/options/annotations.proto -OPENAPIV2_GO=$(OPENAPIV2_PROTO:.proto=.pb.go) - -PKGMAP=Mgoogle/protobuf/descriptor.proto=$(GO_PLUGIN_PKG)/descriptor,Mexamples/proto/sub/message.proto=$(PKG)/examples/proto/sub -ADDITIONAL_GW_FLAGS= -ifneq "$(GATEWAY_PLUGIN_FLAGS)" "" - ADDITIONAL_GW_FLAGS=,$(GATEWAY_PLUGIN_FLAGS) -endif -ADDITIONAL_SWG_FLAGS= -ifneq "$(SWAGGER_PLUGIN_FLAGS)" "" - ADDITIONAL_SWG_FLAGS=,$(SWAGGER_PLUGIN_FLAGS) -endif -SWAGGER_EXAMPLES=examples/proto/examplepb/echo_service.proto \ - examples/proto/examplepb/a_bit_of_everything.proto \ - examples/proto/examplepb/wrappers.proto \ - examples/proto/examplepb/unannotated_echo_service.proto -EXAMPLES=examples/proto/examplepb/echo_service.proto \ - examples/proto/examplepb/a_bit_of_everything.proto \ - examples/proto/examplepb/stream.proto \ - examples/proto/examplepb/flow_combination.proto \ - examples/proto/examplepb/wrappers.proto \ - examples/proto/examplepb/unannotated_echo_service.proto -EXAMPLE_SVCSRCS=$(EXAMPLES:.proto=.pb.go) -EXAMPLE_GWSRCS=$(EXAMPLES:.proto=.pb.gw.go) -EXAMPLE_SWAGGERSRCS=$(SWAGGER_EXAMPLES:.proto=.swagger.json) -EXAMPLE_DEPS=examples/proto/sub/message.proto examples/proto/sub2/message.proto -EXAMPLE_DEPSRCS=$(EXAMPLE_DEPS:.proto=.pb.go) - -EXAMPLE_CLIENT_DIR=examples/clients -ECHO_EXAMPLE_SPEC=examples/proto/examplepb/echo_service.swagger.json -ECHO_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/echo/api_client.go \ - $(EXAMPLE_CLIENT_DIR)/echo/api_response.go \ +EXAMPLE_CLIENT_DIR=examples/internal/clients +ECHO_EXAMPLE_SPEC=examples/internal/proto/examplepb/echo_service.swagger.json +ECHO_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/echo/client.go \ + $(EXAMPLE_CLIENT_DIR)/echo/response.go \ $(EXAMPLE_CLIENT_DIR)/echo/configuration.go \ - $(EXAMPLE_CLIENT_DIR)/echo/echo_service_api.go \ - $(EXAMPLE_CLIENT_DIR)/echo/examplepb_simple_message.go \ - $(EXAMPLE_CLIENT_DIR)/echo/examplepb_embedded.go -ABE_EXAMPLE_SPEC=examples/proto/examplepb/a_bit_of_everything.swagger.json -ABE_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/abe/a_bit_of_everything_nested.go \ - $(EXAMPLE_CLIENT_DIR)/abe/a_bit_of_everything_service_api.go \ - $(EXAMPLE_CLIENT_DIR)/abe/api_client.go \ - $(EXAMPLE_CLIENT_DIR)/abe/api_response.go \ - $(EXAMPLE_CLIENT_DIR)/abe/camel_case_service_name_api.go \ + $(EXAMPLE_CLIENT_DIR)/echo/api_echo_service.go \ + $(EXAMPLE_CLIENT_DIR)/echo/model_examplepb_simple_message.go \ + $(EXAMPLE_CLIENT_DIR)/echo/model_examplepb_embedded.go +ABE_EXAMPLE_SPEC=examples/internal/proto/examplepb/a_bit_of_everything.swagger.json +ABE_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/abe/model_a_bit_of_everything_nested.go \ + $(EXAMPLE_CLIENT_DIR)/abe/api_a_bit_of_everything_service.go \ + $(EXAMPLE_CLIENT_DIR)/abe/client.go \ + $(EXAMPLE_CLIENT_DIR)/abe/api_camel_case_service_name.go \ $(EXAMPLE_CLIENT_DIR)/abe/configuration.go \ - $(EXAMPLE_CLIENT_DIR)/abe/echo_rpc_api.go \ - $(EXAMPLE_CLIENT_DIR)/abe/echo_service_api.go \ - $(EXAMPLE_CLIENT_DIR)/abe/examplepb_a_bit_of_everything.go \ - $(EXAMPLE_CLIENT_DIR)/abe/examplepb_body.go \ - $(EXAMPLE_CLIENT_DIR)/abe/examplepb_numeric_enum.go \ - $(EXAMPLE_CLIENT_DIR)/abe/nested_deep_enum.go \ - $(EXAMPLE_CLIENT_DIR)/abe/protobuf_empty.go \ - $(EXAMPLE_CLIENT_DIR)/abe/sub_string_message.go -UNANNOTATED_ECHO_EXAMPLE_SPEC=examples/proto/examplepb/unannotated_echo_service.swagger.json -UNANNOTATED_ECHO_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/unannotatedecho/api_client.go \ - $(EXAMPLE_CLIENT_DIR)/unannotatedecho/api_response.go \ + $(EXAMPLE_CLIENT_DIR)/abe/api_echo_rpc.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_examplepb_a_bit_of_everything.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_examplepb_a_bit_of_everything_repeated.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_examplepb_body.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_examplepb_numeric_enum.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_examplepb_update_v2_request.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_message_path_enum_nested_path_enum.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_nested_deep_enum.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_pathenum_path_enum.go \ + $(EXAMPLE_CLIENT_DIR)/abe/model_protobuf_field_mask.go \ + $(EXAMPLE_CLIENT_DIR)/abe/response.go +UNANNOTATED_ECHO_EXAMPLE_SPEC=examples/internal/proto/examplepb/unannotated_echo_service.swagger.json +UNANNOTATED_ECHO_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/unannotatedecho/client.go \ + $(EXAMPLE_CLIENT_DIR)/unannotatedecho/response.go \ $(EXAMPLE_CLIENT_DIR)/unannotatedecho/configuration.go \ - $(EXAMPLE_CLIENT_DIR)/unannotatedecho/examplepb_unannotated_simple_message.go \ - $(EXAMPLE_CLIENT_DIR)/unannotatedecho/unannotated_echo_service_api.go -EXAMPLE_CLIENT_SRCS=$(ECHO_EXAMPLE_SRCS) $(ABE_EXAMPLE_SRCS) $(UNANNOTATED_ECHO_EXAMPLE_SRCS) + $(EXAMPLE_CLIENT_DIR)/unannotatedecho/model_examplepb_unannotated_simple_message.go \ + $(EXAMPLE_CLIENT_DIR)/unannotatedecho/api_unannotated_echo_service.go +RESPONSE_BODY_EXAMPLE_SPEC=examples/internal/proto/examplepb/response_body_service.swagger.json +RESPONSE_BODY_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/responsebody/client.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/response.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/configuration.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/model_examplepb_repeated_response_body_out.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/model_examplepb_repeated_response_body_out_response.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/model_examplepb_repeated_response_strings.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/model_examplepb_response_body_out.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/model_examplepb_response_body_out_response.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/model_response_response_type.go \ + $(EXAMPLE_CLIENT_DIR)/responsebody/api_response_body_service.go +GENERATE_UNBOUND_METHODS_EXAMPLE_SPEC=examples/internal/proto/examplepb/generate_unbound_methods.swagger.json +GENERATE_UNBOUND_METHODS_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/generateunboundmethods/client.go \ + $(EXAMPLE_CLIENT_DIR)/generateunboundmethods/response.go \ + $(EXAMPLE_CLIENT_DIR)/generateunboundmethods/configuration.go \ + $(EXAMPLE_CLIENT_DIR)/generateunboundmethods/model_examplepb_generate_unbound_methods_simple_message.go \ + $(EXAMPLE_CLIENT_DIR)/generateunboundmethods/api_generate_unbound_methods.go + +EXAMPLE_CLIENT_SRCS=$(ECHO_EXAMPLE_SRCS) $(ABE_EXAMPLE_SRCS) $(UNANNOTATED_ECHO_EXAMPLE_SRCS) $(RESPONSE_BODY_EXAMPLE_SRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_SRCS) SWAGGER_CODEGEN=swagger-codegen -PROTOC_INC_PATH=$(dir $(shell which protoc))/../include - -generate: $(RUNTIME_GO) - -.SUFFIXES: .go .proto - -$(GO_PLUGIN): - go get $(GO_PLUGIN_PKG) - go build -o $@ $(GO_PLUGIN_PKG) - -$(RUNTIME_GO): $(RUNTIME_PROTO) $(GO_PLUGIN) - protoc -I $(PROTOC_INC_PATH) --plugin=$(GO_PLUGIN) -I $(GOPATH)/src/$(GO_PTYPES_ANY_PKG) -I. --go_out=$(PKGMAP):. $(RUNTIME_PROTO) - -$(OPENAPIV2_GO): $(OPENAPIV2_PROTO) $(GO_PLUGIN) - protoc -I $(PROTOC_INC_PATH) --plugin=$(GO_PLUGIN) -I. --go_out=$(PKGMAP):$(GOPATH)/src $(OPENAPIV2_PROTO) - -$(GATEWAY_PLUGIN): $(RUNTIME_GO) $(GATEWAY_PLUGIN_SRC) - go build -o $@ $(GATEWAY_PLUGIN_PKG) - -$(SWAGGER_PLUGIN): $(SWAGGER_PLUGIN_SRC) $(OPENAPIV2_GO) - go build -o $@ $(SWAGGER_PLUGIN_PKG) - -$(EXAMPLE_SVCSRCS): $(GO_PLUGIN) $(EXAMPLES) - protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP),plugins=grpc:. $(EXAMPLES) -$(EXAMPLE_DEPSRCS): $(GO_PLUGIN) $(EXAMPLE_DEPS) - mkdir -p $(OUTPUT_DIR) - protoc -I $(PROTOC_INC_PATH) -I. --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP),plugins=grpc:$(OUTPUT_DIR) $(@:.pb.go=.proto) - cp $(OUTPUT_DIR)/$(PKG)/$@ $@ || cp $(OUTPUT_DIR)/$@ $@ - -$(EXAMPLE_GWSRCS): ADDITIONAL_GW_FLAGS:=$(ADDITIONAL_GW_FLAGS),grpc_api_configuration=examples/proto/examplepb/unannotated_echo_service.yaml -$(EXAMPLE_GWSRCS): $(GATEWAY_PLUGIN) $(EXAMPLES) - protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GATEWAY_PLUGIN) --grpc-gateway_out=logtostderr=true,$(PKGMAP)$(ADDITIONAL_GW_FLAGS):. $(EXAMPLES) - -$(EXAMPLE_SWAGGERSRCS): ADDITIONAL_SWG_FLAGS:=$(ADDITIONAL_SWG_FLAGS),grpc_api_configuration=examples/proto/examplepb/unannotated_echo_service.yaml -$(EXAMPLE_SWAGGERSRCS): $(SWAGGER_PLUGIN) $(SWAGGER_EXAMPLES) - protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(SWAGGER_PLUGIN) --swagger_out=logtostderr=true,$(PKGMAP)$(ADDITIONAL_SWG_FLAGS):. $(SWAGGER_EXAMPLES) - $(ECHO_EXAMPLE_SRCS): $(ECHO_EXAMPLE_SPEC) $(SWAGGER_CODEGEN) generate -i $(ECHO_EXAMPLE_SPEC) \ - -l go -o examples/clients/echo --additional-properties packageName=echo + -l go -o examples/internal/clients/echo --additional-properties packageName=echo @rm -f $(EXAMPLE_CLIENT_DIR)/echo/README.md \ - $(EXAMPLE_CLIENT_DIR)/echo/git_push.sh \ - $(EXAMPLE_CLIENT_DIR)/echo/.travis.yml + $(EXAMPLE_CLIENT_DIR)/echo/git_push.sh $(ABE_EXAMPLE_SRCS): $(ABE_EXAMPLE_SPEC) $(SWAGGER_CODEGEN) generate -i $(ABE_EXAMPLE_SPEC) \ - -l go -o examples/clients/abe --additional-properties packageName=abe + -l go -o examples/internal/clients/abe --additional-properties packageName=abe @rm -f $(EXAMPLE_CLIENT_DIR)/abe/README.md \ - $(EXAMPLE_CLIENT_DIR)/abe/git_push.sh \ - $(EXAMPLE_CLIENT_DIR)/abe/.travis.yml + $(EXAMPLE_CLIENT_DIR)/abe/git_push.sh $(UNANNOTATED_ECHO_EXAMPLE_SRCS): $(UNANNOTATED_ECHO_EXAMPLE_SPEC) $(SWAGGER_CODEGEN) generate -i $(UNANNOTATED_ECHO_EXAMPLE_SPEC) \ - -l go -o examples/clients/unannotatedecho --additional-properties packageName=unannotatedecho + -l go -o examples/internal/clients/unannotatedecho --additional-properties packageName=unannotatedecho @rm -f $(EXAMPLE_CLIENT_DIR)/unannotatedecho/README.md \ - $(EXAMPLE_CLIENT_DIR)/unannotatedecho/git_push.sh \ - $(EXAMPLE_CLIENT_DIR)/unannotatedecho/.travis.yml - -examples: $(EXAMPLE_SVCSRCS) $(EXAMPLE_GWSRCS) $(EXAMPLE_DEPSRCS) $(EXAMPLE_SWAGGERSRCS) $(EXAMPLE_CLIENT_SRCS) -test: examples - go test -race $(PKG)/... - go test -race $(PKG)/examples/integration -args -network=unix -endpoint=test.sock - -lint: - golint --set_exit_status $(PKG)/runtime - golint --set_exit_status $(PKG)/utilities/... - golint --set_exit_status $(PKG)/protoc-gen-grpc-gateway/... - golint --set_exit_status $(PKG)/protoc-gen-swagger/... - go vet $(PKG)/runtime || true - go vet $(PKG)/utilities/... - go vet $(PKG)/protoc-gen-grpc-gateway/... - go vet $(PKG)/protoc-gen-swagger/... + $(EXAMPLE_CLIENT_DIR)/unannotatedecho/git_push.sh +$(RESPONSE_BODY_EXAMPLE_SRCS): $(RESPONSE_BODY_EXAMPLE_SPEC) + $(SWAGGER_CODEGEN) generate -i $(RESPONSE_BODY_EXAMPLE_SPEC) \ + -l go -o examples/internal/clients/responsebody --additional-properties packageName=responsebody + @rm -f $(EXAMPLE_CLIENT_DIR)/responsebody/README.md \ + $(EXAMPLE_CLIENT_DIR)/responsebody/git_push.sh +$(GENERATE_UNBOUND_METHODS_EXAMPLE_SRCS): $(GENERATE_UNBOUND_METHODS_EXAMPLE_SPEC) + $(SWAGGER_CODEGEN) generate -i $(GENERATE_UNBOUND_METHODS_EXAMPLE_SPEC) \ + -l go -o examples/internal/clients/generateunboundmethods --additional-properties packageName=generateunboundmethods + @rm -f $(EXAMPLE_CLIENT_DIR)/generateunboundmethods/README.md \ + $(EXAMPLE_CLIENT_DIR)/generateunboundmethods/git_push.sh + +install: + go install github.com/bufbuild/buf/cmd/buf@v1.3.1 + go install \ + ./protoc-gen-openapiv2 \ + ./protoc-gen-grpc-gateway + +proto: + # These generation steps are run in order so that later steps can + # overwrite files produced by previous steps, if necessary. + buf generate + # Remove generated gateway in runtime tests, causes import cycle + rm ./runtime/internal/examplepb/non_standard_names.pb.gw.go + # Remove generated_input.proto files, bazel genrule relies on these + # *not* being generated (to avoid conflicts). + rm ./examples/internal/proto/examplepb/generated_input.pb.go + rm ./examples/internal/proto/examplepb/generated_input_grpc.pb.go + rm ./examples/internal/proto/examplepb/generated_input.pb.gw.go + buf generate \ + --template ./examples/internal/proto/examplepb/openapi_merge.buf.gen.yaml \ + --path ./examples/internal/proto/examplepb/openapi_merge_a.proto \ + --path ./examples/internal/proto/examplepb/openapi_merge_b.proto + buf generate \ + --template ./examples/internal/proto/examplepb/standalone_echo_service.buf.gen.yaml \ + --path examples/internal/proto/examplepb/unannotated_echo_service.proto + mv examples/internal/proto/examplepb/unannotated_echo_service.pb.gw.go examples/internal/proto/standalone/ + buf generate \ + --template ./examples/internal/proto/examplepb/unannotated_echo_service.buf.gen.yaml \ + --path examples/internal/proto/examplepb/unannotated_echo_service.proto + buf generate \ + --template ./examples/internal/proto/examplepb/generate_unbound_methods.buf.gen.yaml \ + --path examples/internal/proto/examplepb/generate_unbound_methods.proto + buf generate \ + --template ./examples/internal/proto/examplepb/use_go_template.buf.gen.yaml \ + --path examples/internal/proto/examplepb/use_go_template.proto + buf generate \ + --template ./examples/internal/proto/examplepb/visibility_rule_preview_echo_service.buf.gen.yaml \ + --path examples/internal/proto/examplepb/visibility_rule_echo_service.proto + mv examples/internal/proto/examplepb/visibility_rule_echo_service.swagger.json examples/internal/proto/examplepb/visibility_rule_preview_echo_service.swagger.json + buf generate \ + --template ./examples/internal/proto/examplepb/visibility_rule_internal_echo_service.buf.gen.yaml \ + --path examples/internal/proto/examplepb/visibility_rule_echo_service.proto + mv examples/internal/proto/examplepb/visibility_rule_echo_service.swagger.json examples/internal/proto/examplepb/visibility_rule_internal_echo_service.swagger.json + buf generate \ + --template ./examples/internal/proto/examplepb/visibility_rule_none_echo_service.buf.gen.yaml \ + --path examples/internal/proto/examplepb/visibility_rule_echo_service.proto + mv examples/internal/proto/examplepb/visibility_rule_echo_service.swagger.json examples/internal/proto/examplepb/visibility_rule_none_echo_service.swagger.json + buf generate \ + --template ./examples/internal/proto/examplepb/visibility_rule_preview_and_internal_echo_service.buf.gen.yaml \ + --path examples/internal/proto/examplepb/visibility_rule_echo_service.proto + mv examples/internal/proto/examplepb/visibility_rule_echo_service.swagger.json examples/internal/proto/examplepb/visibility_rule_preview_and_internal_echo_service.swagger.json + +generate: proto $(ECHO_EXAMPLE_SRCS) $(ABE_EXAMPLE_SRCS) $(UNANNOTATED_ECHO_EXAMPLE_SRCS) $(RESPONSE_BODY_EXAMPLE_SRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_SRCS) + +test: proto + go test -short -race ./... + go test -race ./examples/internal/integration -args -network=unix -endpoint=test.sock clean: - rm -f $(GATEWAY_PLUGIN) $(SWAGGER_PLUGIN) -distclean: clean - rm -f $(GO_PLUGIN) -realclean: distclean - rm -f $(EXAMPLE_SVCSRCS) $(EXAMPLE_DEPSRCS) - rm -f $(EXAMPLE_GWSRCS) - rm -f $(EXAMPLE_SWAGGERSRCS) + find . -type f -name '*.pb.go' -delete + find . -type f -name '*.swagger.json' -delete + find . -type f -name '*.pb.gw.go' -delete rm -f $(EXAMPLE_CLIENT_SRCS) - rm -f $(OPENAPIV2_GO) -.PHONY: generate examples test lint clean distclean realclean +.PHONY: generate test clean proto install diff --git a/README.md b/README.md index ccb7d7409c4..317e3c97c5c 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,237 @@ -# grpc-gateway +
+

gRPC-Gateway

+

+gRPC to JSON proxy generator following the gRPC HTTP spec +

+ + + + + + -[![Build Status](https://travis-ci.org/grpc-ecosystem/grpc-gateway.svg?branch=master)](https://travis-ci.org/grpc-ecosystem/grpc-gateway) +
-grpc-gateway is a plugin of [protoc](http://github.com/google/protobuf). -It reads [gRPC](http://github.com/grpc/grpc-common) service definition, -and generates a reverse-proxy server which translates a RESTful JSON API into gRPC. -This server is generated according to [custom options](https://cloud.google.com/service-management/reference/rpc/google.api#http) in your gRPC definition. +## About -It helps you to provide your APIs in both gRPC and RESTful style at the same time. +The gRPC-Gateway is a plugin of the Google protocol buffers compiler +[protoc](https://github.com/protocolbuffers/protobuf). +It reads protobuf service definitions and generates a reverse-proxy server which +translates a RESTful HTTP API into gRPC. This server is generated according to the +[`google.api.http`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L46) +annotations in your service definitions. -![architecture introduction diagram](https://docs.google.com/drawings/d/12hp4CPqrNPFhattL_cIoJptFvlAqm5wLQ0ggqI5mkCg/pub?w=749&h=370) +This helps you provide your APIs in both gRPC and RESTful style at the same time. -## Check out our [documentation](https://grpc-ecosystem.github.io/grpc-gateway/)! +
+ +
+ +## Docs + +You can read our docs at: + +- https://grpc-ecosystem.github.io/grpc-gateway/ + +## Testimonials + +> We use the gRPC-Gateway to serve millions of API requests per day, +> and have been since 2018 and through all of that, +> we have never had any issues with it. +> +> _- William Mill, [Ad Hoc](http://adhocteam.us/)_ ## Background -gRPC is great -- it generates API clients and server stubs in many programming languages, it is fast, easy-to-use, bandwidth-efficient and its design is combat-proven by Google. -However, you might still want to provide a traditional RESTful API as well. Reasons can range from maintaining backwards-compatibility, supporting languages or clients not well supported by gRPC to simply maintaining the aesthetics and tooling involved with a RESTful architecture. -This project aims to provide that HTTP+JSON interface to your gRPC service. A small amount of configuration in your service to attach HTTP semantics is all that's needed to generate a reverse-proxy with this library. +gRPC is great -- it generates API clients and server stubs in many programming +languages, it is fast, easy-to-use, bandwidth-efficient and its design is +combat-proven by Google. However, you might still want to provide a traditional +RESTful JSON API as well. Reasons can range from maintaining +backward-compatibility, supporting languages or clients that are not well supported by +gRPC, to simply maintaining the aesthetics and tooling involved with a RESTful +JSON architecture. + +This project aims to provide that HTTP+JSON interface to your gRPC service. +A small amount of configuration in your service to attach HTTP semantics is all +that's needed to generate a reverse-proxy with this library. ## Installation -First you need to install ProtocolBuffers 3.0.0-beta-3 or later. -```sh -mkdir tmp -cd tmp -git clone https://github.com/google/protobuf -cd protobuf -./autogen.sh -./configure -make -make check -sudo make install +### Compile from source +The following instructions assume you are using +[Go Modules](https://github.com/golang/go/wiki/Modules) for dependency +management. Use a +[tool dependency](https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module) +to track the versions of the following executable packages: + +```go +// +build tools + +package tools + +import ( + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" + _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" + _ "google.golang.org/protobuf/cmd/protoc-gen-go" +) ``` -Then, `go get -u` as usual the following packages: +Run `go mod tidy` to resolve the versions. Install by running ```sh -go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway -go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger -go get -u github.com/golang/protobuf/protoc-gen-go +$ go install \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/protobuf/cmd/protoc-gen-go \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc ``` - + +This will place four binaries in your `$GOBIN`; + +- `protoc-gen-grpc-gateway` +- `protoc-gen-openapiv2` +- `protoc-gen-go` +- `protoc-gen-go-grpc` + +Make sure that your `$GOBIN` is in your `$PATH`. + +### Download the binaries + +You may alternatively download the binaries from the [GitHub releases page](https://github.com/grpc-ecosystem/grpc-gateway/releases/latest). +We generate [SLSA3 signatures](slsa.dev) using the OpenSSF's [slsa-framework/slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator) during the release process. To verify a release binary: +1. Install the verification tool from [slsa-framework/slsa-verifier#installation](https://github.com/slsa-framework/slsa-verifier#installation). +2. Download the provenance file `attestation.intoto.jsonl` from the [GitHub releases page](https://github.com/grpc-ecosystem/grpc-gateway/releases/latest). +3. Run the verifier: +```shell +slsa-verifier -artifact-path -provenance attestation.intoto.jsonl -source github.com/grpc-ecosystem/grpc-gateway -tag +``` + +Alternatively, see the section on remotely managed plugin versions below. + ## Usage -Make sure that your `$GOPATH/bin` is in your `$PATH`. -1. Define your service in gRPC - - your_service.proto: +1. Define your [gRPC](https://grpc.io/docs/) service using protocol buffers + + `your_service.proto`: + ```protobuf - syntax = "proto3"; - package example; - message StringMessage { - string value = 1; - } - - service YourService { - rpc Echo(StringMessage) returns (StringMessage) {} - } + syntax = "proto3"; + package your.service.v1; + option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1"; + + message StringMessage { + string value = 1; + } + + service YourService { + rpc Echo(StringMessage) returns (StringMessage) {} + } ``` -2. Add a [custom option](https://cloud.google.com/service-management/reference/rpc/google.api#http) to the .proto file - - your_service.proto: + +2. Generate gRPC stubs + + This step generates the gRPC stubs that you can use to implement the service and consume from clients: + + Here's an example `buf.gen.yaml` you can use to generate the stubs with [buf](https://github.com/bufbuild/buf): + + ```yaml + version: v1 + plugins: + - name: go + out: gen/go + opt: + - paths=source_relative + - name: go-grpc + out: gen/go + opt: + - paths=source_relative + ``` + + With this file in place, you can generate your files using `buf generate`. + + > For a complete example of using `buf generate` to generate protobuf stubs, see + > [the boilerplate repo](https://github.com/johanbrandhorst/grpc-gateway-boilerplate). + > For more information on generating the stubs with buf, see + > [the official documentation](https://docs.buf.build/generate-usage). + + If you are using `protoc` to generate stubs, here's an example of what a command + might look like: + + ```sh + protoc -I . \ + --go_out ./gen/go/ --go_opt paths=source_relative \ + --go-grpc_out ./gen/go/ --go-grpc_opt paths=source_relative \ + your/service/v1/your_service.proto + ``` + +3. Implement your service in gRPC as usual. + +4. Generate reverse-proxy using `protoc-gen-grpc-gateway` + + At this point, you have 3 options: + + - no further modifications, use the default mapping to HTTP semantics (method, path, etc.) + - this will work on any `.proto` file, but will not allow setting HTTP paths, request parameters or similar + - additional `.proto` modifications to use a custom mapping + - relies on parameters in the `.proto` file to set custom HTTP mappings + - no `.proto` modifications, but use an external configuration file + - relies on an external configuration file to set custom HTTP mappings + - mostly useful when the source proto file isn't under your control + + 1. Using the default mapping + + This requires no additional modification to the `.proto` file but does require enabling a specific option when executing the plugin. + The `generate_unbound_methods` should be enabled. + + Here's what a `buf.gen.yaml` file might look like with this option enabled: + + ```yaml + version: v1 + plugins: + - name: go + out: gen/go + opt: + - paths=source_relative + - name: go-grpc + out: gen/go + opt: + - paths=source_relative + - name: grpc-gateway + out: gen/go + opt: + - paths=source_relative + - generate_unbound_methods=true + ``` + + With `protoc` (just the grpc-gateway stubs): + + ```sh + protoc -I . --grpc-gateway_out ./gen/go \ + --grpc-gateway_opt logtostderr=true \ + --grpc-gateway_opt paths=source_relative \ + --grpc-gateway_opt generate_unbound_methods=true \ + your/service/v1/your_service.proto + ``` + + 2. With custom annotations + + Add a [`google.api.http`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L46) + annotation to your .proto file + + `your_service.proto`: + ```diff syntax = "proto3"; - package example; + package your.service.v1; + option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1"; + +import "google/api/annotations.proto"; + message StringMessage { string value = 1; } - + service YourService { - rpc Echo(StringMessage) returns (StringMessage) {} + rpc Echo(StringMessage) returns (StringMessage) { @@ -83,170 +243,369 @@ Make sure that your `$GOPATH/bin` is in your `$PATH`. } ``` - If you do not want to modify the proto file for use with grpc-gateway you can alternatively use an external [gRPC API Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) file. [Check our documentation](https://grpc-ecosystem.github.io/grpc-gateway/docs/grpcapiconfiguration.html) for more information. + > You will need to provide the required third party protobuf files to the protobuf compiler. + > If you are using [buf](https://github.com/bufbuild/buf), this dependency can + > be added to the `deps` array in your `buf.yaml` under the name + > `buf.build/googleapis/googleapis`: + > ```yaml + > version: v1 + > name: buf.build/yourorg/myprotos + > deps: + > - buf.build/googleapis/googleapis + > ``` + > Always run `buf mod update` after adding a dependency to your `buf.yaml`. + + See [a_bit_of_everything.proto](examples/internal/proto/examplepb/a_bit_of_everything.proto) + for examples of more annotations you can add to customize gateway behavior + and generated OpenAPI output. + + Here's what a `buf.gen.yaml` file might look like: + + ```yaml + version: v1 + plugins: + - name: go + out: gen/go + opt: + - paths=source_relative + - name: go-grpc + out: gen/go + opt: + - paths=source_relative + - name: grpc-gateway + out: gen/go + opt: + - paths=source_relative + ``` + + If you are using `protoc` to generate stubs, you need to ensure the required + dependencies are available to the compiler at compile time. These can be found + by manually cloning and copying the relevant files from the + [googleapis repository](https://github.com/googleapis/googleapis), and providing + them to `protoc` when running. The files you will need are: + + ``` + google/api/annotations.proto + google/api/field_behavior.proto + google/api/http.proto + google/api/httpbody.proto + ``` + + Here's what a `protoc` execution might look like: -3. Generate gRPC stub - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --go_out=plugins=grpc:. \ - path/to/your_service.proto - ``` - - It will generate a stub file `path/to/your_service.pb.go`. -4. Implement your service in gRPC as usual - 1. (Optional) Generate gRPC stub in the language you want. - - e.g. - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --ruby_out=. \ - path/to/your/service_proto - - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --plugin=protoc-gen-grpc=grpc_ruby_plugin \ - --grpc-ruby_out=. \ - path/to/your/service.proto - ``` - 2. Add the googleapis-common-protos gem (or your language equivalent) as a dependency to your project. - 3. Implement your service - -5. Generate reverse-proxy - + protoc -I . --grpc-gateway_out ./gen/go \ + --grpc-gateway_opt logtostderr=true \ + --grpc-gateway_opt paths=source_relative \ + your/service/v1/your_service.proto + ``` + + 3. External configuration + If you do not want to (or cannot) modify the proto file for use with gRPC-Gateway you can + alternatively use an external + [gRPC Service Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) file. + [Check our documentation](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/grpc_api_configuration/) + for more information. This is best combined with the `standalone=true` option + to generate a file that can live in its own package, separate from the files + generated by the source protobuf file. + + Here's what a `buf.gen.yaml` file might look like with this option enabled: + + ```yaml + version: v1 + plugins: + - name: go + out: gen/go + opt: + - paths=source_relative + - name: go-grpc + out: gen/go + opt: + - paths=source_relative + - name: grpc-gateway + out: gen/go + opt: + - paths=source_relative + - grpc_api_configuration=path/to/config.yaml + - standalone=true + ``` + + With `protoc` (just the grpc-gateway stubs): + ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --grpc-gateway_out=logtostderr=true:. \ - path/to/your_service.proto + protoc -I . --grpc-gateway_out ./gen/go \ + --grpc-gateway_opt logtostderr=true \ + --grpc-gateway_opt paths=source_relative \ + --grpc-gateway_opt grpc_api_configuration=path/to/config.yaml \ + --grpc-gateway_opt standalone=true \ + your/service/v1/your_service.proto ``` - - It will generate a reverse proxy `path/to/your_service.pb.gw.go`. - Note: After generating the code for each of the stubs, in order to build the code, you will want to run ```go get .``` from the directory containing the stubs. +5. Write an entrypoint for the HTTP reverse-proxy server -6. Write an entrypoint - - Now you need to write an entrypoint of the proxy server. ```go package main import ( + "context" "flag" "net/http" - + "github.com/golang/glog" - "golang.org/x/net/context" - "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "google.golang.org/grpc" - - gw "path/to/your_service_package" + "google.golang.org/grpc/credentials/insecure" + + gw "github.com/yourorg/yourrepo/proto/gen/go/your/service/v1/your_service" // Update ) - + var ( - echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of YourService") + // command-line options: + // gRPC server endpoint + grpcServerEndpoint = flag.String("grpc-server-endpoint", "localhost:9090", "gRPC server endpoint") ) - + func run() error { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) defer cancel() - + + // Register gRPC server endpoint + // Note: Make sure the gRPC server is running properly and accessible mux := runtime.NewServeMux() - opts := []grpc.DialOption{grpc.WithInsecure()} - err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts) + opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} + err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts) if err != nil { return err } - - return http.ListenAndServe(":8080", mux) + + // Start HTTP server (and proxy calls to gRPC server endpoint) + return http.ListenAndServe(":8081", mux) } - + func main() { flag.Parse() defer glog.Flush() - + if err := run(); err != nil { glog.Fatal(err) } } ``` -7. (Optional) Generate swagger definitions +6. (Optional) Generate OpenAPI definitions using `protoc-gen-openapiv2` + + Here's what a `buf.gen.yaml` file might look like: + + ```yaml + version: v1 + plugins: + - name: go + out: gen/go + opt: + - paths=source_relative + - name: go-grpc + out: gen/go + opt: + - paths=source_relative + - name: grpc-gateway + out: gen/go + opt: + - paths=source_relative + - name: openapiv2 + out: gen/openapiv2 + ``` + + To use the custom protobuf annotations supported by `protoc-gen-openapiv2`, we need + another dependency added to our protobuf generation step. If you are using + `buf`, you can add the `buf.build/grpc-ecosystem/grpc-gateway` dependency + to your `deps` array: + ```yaml + version: v1 + name: buf.build/yourorg/myprotos + deps: + - buf.build/googleapis/googleapis + - buf.build/grpc-ecosystem/grpc-gateway + ``` + + With `protoc` (just the swagger file): ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --swagger_out=logtostderr=true:. \ - path/to/your_service.proto + protoc -I . --openapiv2_out ./gen/openapiv2 \ + --openapiv2_opt logtostderr=true \ + your/service/v1/your_service.proto ``` + If you are using `protoc` to generate stubs, you will need to copy the protobuf + files from the `protoc-gen-openapiv2/options` directory of this repository, + and providing them to `protoc` when running. + + Note that this plugin also supports generating OpenAPI definitions for unannotated methods; + use the `generate_unbound_methods` option to enable this. + + It is possible with the HTTP mapping for a gRPC service method to create duplicate mappings + with the only difference being constraints on the path parameter. + + `/v1/{name=projects/*}` and `/v1/{name=organizations/*}` both become `/v1/{name}`. When + this occurs the plugin will rename the path parameter with a "_1" (or "_2" etc) suffix + to differentiate the different operations. So in the above example, the 2nd path would become + `/v1/{name_1=organizations/*}`. This can also cause OpenAPI clients to URL encode the "/" that is + part of the path parameter as that is what OpenAPI defines in the specification. To allow gRPC gateway to + accept the URL encoded slash and still route the request, use the UnescapingModeAllCharacters or + UnescapingModeLegacy (which is the default currently though may change in future versions). See + [Customizing Your Gateway](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/customizing_your_gateway/) + for more information. + +## Usage with remote plugins + +As an alternative to all of the above, you can use `buf` with +[remote plugins](https://docs.buf.build/configuration/v1/buf-gen-yaml#name-or-remote) +to manage plugin versions and generation. An example `buf.gen.yaml` using remote +plugin generation looks like this: + +```yaml +version: v1 +plugins: + - remote: buf.build/library/plugins/go:v1.27.1-1 + out: gen/go + opt: + - paths=source_relative + - remote: buf.build/library/plugins/go-grpc:v1.1.0-2 + out: gen/go + opt: + - paths=source_relative + - remote: buf.build/grpc-ecosystem/plugins/grpc-gateway:v2.6.0-1 + out: gen/go + opt: + - paths=source_relative + - remote: buf.build/grpc-ecosystem/plugins/openapiv2:v2.6.0-1 + out: gen/openapiv2 +``` + +This requires no local installation of any plugins. Be careful to use the same +version of the generator as the runtime library, i.e. if using `v2.6.0-1`, run + +```shell +$ go get github.com/grpc-ecosystem/grpc-gateway/v2@v2.6.0 +``` + +To get the same version of the runtime in your `go.mod`. + +## Video intro + +This GopherCon UK 2019 presentation from our maintainer [@JohanBrandhorst](https://github.com/johanbrandhorst) provides a good intro to using the gRPC-Gateway. It uses the following boilerplate repo as a base: https://github.com/johanbrandhorst/grpc-gateway-boilerplate. + +
+ + + +
+ ## Parameters and flags -`protoc-gen-grpc-gateway` supports custom mapping from Protobuf `import` to Golang import path. -They are compatible to [the parameters with same names in `protoc-gen-go`](https://github.com/golang/protobuf#parameters). -In addition we also support the `request_context` parameter in order to use the `http.Request`'s Context (only for Go 1.7 and above). -This parameter can be useful to pass request scoped context between the gateway and the gRPC service. +When using `buf` to generate stubs, flags and parameters are passed through +the `opt` field in your `buf.gen.yaml` file, for example: + +```yaml +version: v1 +plugins: + - name: grpc-gateway + out: gen/go + opt: + - paths=source_relative + - grpc_api_configuration=path/to/config.yaml + - standalone=true +``` + +During code generation with `protoc`, flags to gRPC-Gateway tools must be passed +through `protoc` using one of 2 patterns: -`protoc-gen-grpc-gateway` also supports some more command line flags to control logging. You can give these flags together with parameters above. Run `protoc-gen-grpc-gateway --help` for more details about the flags. +- as part of the `--_out` `protoc` parameter: `--_out=:` -## More Examples -More examples are available under `examples` directory. -* `proto/examplepb/echo_service.proto`, `proto/examplepb/a_bit_of_everything.proto`, `proto/examplepb/unannotated_echo_service.proto`: service definition - * `proto/examplepb/echo_service.pb.go`, `proto/examplepb/a_bit_of_everything.pb.go`, `proto/examplepb/unannotated_echo_service.pb.go`: [generated] stub of the service - * `proto/examplepb/echo_service.pb.gw.go`, `proto/examplepb/a_bit_of_everything.pb.gw.go`, `proto/examplepb/uannotated_echo_service.pb.gw.go`: [generated] reverse proxy for the service - * `proto/examplepb/unannotated_echo_service.yaml`: gRPC API Configuration for ```unannotated_echo_service.proto``` -* `server/main.go`: service implementation -* `main.go`: entrypoint of the generated reverse proxy +```sh +--grpc-gateway_out=logtostderr=true,repeated_path_param_separator=ssv:. +--openapiv2_out=logtostderr=true,repeated_path_param_separator=ssv:. +``` -To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), gRPC-gateway, and a gRPC server, see [this code example by CoreOS](https://github.com/philips/grpc-gateway-example/blob/master/cmd/serve.go) (and its accompanying [blog post](https://coreos.com/blog/gRPC-protobufs-swagger.html)) +- using additional `--_opt` parameters: `--_opt=[,]*` + +```sh +--grpc-gateway_opt logtostderr=true,repeated_path_param_separator=ssv +# or separately +--grpc-gateway_opt logtostderr=true --grpc-gateway_opt repeated_path_param_separator=ssv +--openapiv2_opt logtostderr=true,repeated_path_param_separator=ssv +# or separately +--openapiv2_opt logtostderr=true --openapiv2_opt repeated_path_param_separator=ssv +``` + +## More examples + +More examples are available under the `examples` directory. + +- `proto/examplepb/echo_service.proto`, `proto/examplepb/a_bit_of_everything.proto`, `proto/examplepb/unannotated_echo_service.proto`: service definition + - `proto/examplepb/echo_service.pb.go`, `proto/examplepb/a_bit_of_everything.pb.go`, `proto/examplepb/unannotated_echo_service.pb.go`: [generated] stub of the service + - `proto/examplepb/echo_service.pb.gw.go`, `proto/examplepb/a_bit_of_everything.pb.gw.go`, `proto/examplepb/uannotated_echo_service.pb.gw.go`: [generated] reverse proxy for the service + - `proto/examplepb/unannotated_echo_service.yaml`: gRPC API Configuration for `unannotated_echo_service.proto` +- `server/main.go`: service implementation +- `main.go`: entrypoint of the generated reverse proxy + +To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), +gRPC-Gateway, and a gRPC server, see +[this example by CoreOS](https://github.com/philips/grpc-gateway-example/blob/master/cmd/serve.go) +(and its accompanying [blog post](https://coreos.com/blog/grpc-protobufs-swagger.html)). ## Features + ### Supported -* Generating JSON API handlers -* Method parameters in request body -* Method parameters in request path -* Method parameters in query string -* Enum fields in path parameter (including repeated enum fields). -* Mapping streaming APIs to newline-delimited JSON streams -* Mapping HTTP headers with `Grpc-Metadata-` prefix to gRPC metadata (prefixed with `grpcgateway-`) -* Optionally emitting API definition for [Swagger](http://swagger.io). -* Setting [gRPC timeouts](http://www.grpc.io/docs/guides/wire.html) through inbound HTTP `Grpc-Timeout` header. -* Partial support for [gRPC API Configuration]((https://cloud.google.com/endpoints/docs/grpc/grpc-service-config)) files as an alternative to annotation. - -### Want to support -But not yet. -* Optionally generating the entrypoint. #8 -* `import_path` parameter + +- Generating JSON API handlers. +- Method parameters in the request body. +- Method parameters in the request path. +- Method parameters in the query string. +- Enum fields in the path parameter (including repeated enum fields). +- Mapping streaming APIs to newline-delimited JSON streams. +- Mapping HTTP headers with `Grpc-Metadata-` prefix to gRPC metadata (prefixed with `grpcgateway-`) +- Optionally emitting API definitions for + [OpenAPI (Swagger) v2](https://swagger.io/docs/specification/2-0/basic-structure/). +- Setting [gRPC timeouts](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests) + through inbound HTTP `Grpc-Timeout` header. +- Partial support for [gRPC API Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) + files as an alternative to annotation. +- Automatically translating PATCH requests into Field Mask gRPC requests. See + [the docs](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/patch_feature/) + for more information. ### No plan to support -But patch is welcome. -* Method parameters in HTTP headers -* Handling trailer metadata -* Encoding request/response body in XML -* True bi-directional streaming. (Probably impossible?) -# Mapping gRPC to HTTP +But patches are welcome. -* [How gRPC error codes map to HTTP status codes in the response](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/errors.go#L15) -* HTTP request source IP is added as `X-Forwarded-For` gRPC request header -* HTTP request host is added as `X-Forwarded-Host` gRPC request header -* HTTP `Authorization` header is added as `authorization` gRPC request header -* Remaining Permanent HTTP header keys (as specified by the IANA [here](http://www.iana.org/assignments/message-headers/message-headers.xhtml) are prefixed with `grpcgateway-` and added with their values to gRPC request header -* HTTP headers that start with 'Grpc-Metadata-' are mapped to gRPC metadata (prefixed with `grpcgateway-`) -* While configurable, the default {un,}marshaling uses [jsonpb](https://godoc.org/github.com/golang/protobuf/jsonpb) with `OrigName: true`. +- Method parameters in HTTP headers. +- Handling trailer metadata. +- Encoding request/response body in XML. +- True bi-directional streaming. +## Mapping gRPC to HTTP + +- [How gRPC error codes map to HTTP status codes in the response](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/errors.go#L15). +- HTTP request source IP is added as `X-Forwarded-For` gRPC request header. +- HTTP request host is added as `X-Forwarded-Host` gRPC request header. +- HTTP `Authorization` header is added as `authorization` gRPC request header. +- Remaining Permanent HTTP header keys (as specified by the IANA + [here](http://www.iana.org/assignments/message-headers/message-headers.xhtml)) + are prefixed with `grpcgateway-` and added with their values to gRPC request + header. +- HTTP headers that start with 'Grpc-Metadata-' are mapped to gRPC metadata + (prefixed with `grpcgateway-`). +- While configurable, the default {un,}marshaling uses + [protojson](https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson). +- The path template used to map gRPC service methods to HTTP endpoints supports the [google.api.http](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto) + path template syntax. For example, `/api/v1/{name=projects/*/topics/*}` or `/prefix/{path=organizations/**}`. + +## Contribution -# Contribution See [CONTRIBUTING.md](http://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md). -# License -grpc-gateway is licensed under the BSD 3-Clause License. +## License + +gRPC-Gateway is licensed under the BSD 3-Clause License. See [LICENSE.txt](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt) for more details. diff --git a/WORKSPACE b/WORKSPACE index 225d0181767..c7a81b0d2dd 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1,53 +1,91 @@ workspace(name = "grpc_ecosystem_grpc_gateway") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") + +# Define before rules_proto, otherwise we receive the version of com_google_protobuf from there http_archive( - name = "io_bazel_rules_go", - url = "https://github.com/bazelbuild/rules_go/releases/download/0.10.3/rules_go-0.10.3.tar.gz", - sha256 = "feba3278c13cde8d67e341a837f69a029f698d7a27ddbb2a202be7a10b22142a", + name = "com_google_protobuf", + sha256 = "1add10f9bd92775b91f326da259f243881e904dd509367d5031d4c782ba82810", + strip_prefix = "protobuf-3.21.9", + urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.21.9.tar.gz"], +) + +http_archive( + name = "bazel_skylib", + sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + ], ) +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") + +bazel_skylib_workspace() + http_archive( + name = "rules_proto", + sha256 = "66bfdf8782796239d3875d37e7de19b1d94301e8972b3cbd2446b332429b4df1", + strip_prefix = "rules_proto-4.0.0", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz", + "https://github.com/bazelbuild/rules_proto/archive/refs/tags/4.0.0.tar.gz", + ], +) + +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") + +rules_proto_dependencies() + +rules_proto_toolchains() + +http_archive( + name = "io_bazel_rules_go", + sha256 = "ae013bf35bd23234d1dea46b079f1e05ba74ac0321423830119d3e787ec73483", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.36.0/rules_go-v0.36.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.36.0/rules_go-v0.36.0.zip", + ], +) + +git_repository( name = "bazel_gazelle", - url = "https://github.com/bazelbuild/bazel-gazelle/releases/download/0.10.1/bazel-gazelle-0.10.1.tar.gz", - sha256 = "d03625db67e9fb0905bbd206fa97e32ae9da894fe234a493e7517fd25faec914", + commit = "f377e6eff8e24508feb1a34b1e5e681982482a9f", + remote = "https://github.com/bazelbuild/bazel-gazelle", + shallow_since = "1648046534 -0400", ) -load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains") +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") go_rules_dependencies() -go_register_toolchains() +go_register_toolchains(version = "1.17.2") load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") -gazelle_dependencies() +# Use gazelle to declare Go dependencies in Bazel. +# gazelle:repository_macro repositories.bzl%go_repositories -load("@io_bazel_rules_go//go:def.bzl", "go_repository") +load("//:repositories.bzl", "go_repositories") -go_repository( - name = "com_github_rogpeppe_fastuuid", - commit = "6724a57986aff9bff1a1770e9347036def7c89f6", - importpath = "github.com/rogpeppe/fastuuid", -) +go_repositories() -go_repository( - name = "com_github_go_resty_resty", - commit = "f8815663de1e64d57cdd4ee9e2b2fa96977a030e", - importpath = "github.com/go-resty/resty", -) +# This must be invoked after our explicit dependencies +# See https://github.com/bazelbuild/bazel-gazelle/issues/1115. +gazelle_dependencies() -go_repository( - name = "com_github_ghodss_yaml", - commit = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7", - importpath = "github.com/ghodss/yaml", -) +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") -go_repository( - name = "in_gopkg_yaml_v2", - commit = "eb3733d160e74a9c7e442f435eb3bea458e1d19f", - importpath = "gopkg.in/yaml.v2", +protobuf_deps() + +http_archive( + name = "com_github_bazelbuild_buildtools", + sha256 = "e3bb0dc8b0274ea1aca75f1f8c0c835adbe589708ea89bf698069d0790701ea3", + strip_prefix = "buildtools-5.1.0", + urls = ["https://github.com/bazelbuild/buildtools/archive/5.1.0.tar.gz"], ) -load("//:repositories.bzl", "repositories") +load("@com_github_bazelbuild_buildtools//buildifier:deps.bzl", "buildifier_dependencies") -repositories() +buildifier_dependencies() diff --git a/bin/.gitignore b/bin/.gitignore index f20d08cf066..f56721fd9bd 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -1,3 +1,4 @@ /protoc-gen-go +/protoc-gen-go-grpc /protoc-gen-grpc-gateway -/protoc-gen-swagger +/protoc-gen-openapiv2 diff --git a/bin/coverage b/bin/coverage deleted file mode 100755 index 65bbe95faa5..00000000000 --- a/bin/coverage +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -euo pipefail -USE_BAZEL=${USE_BAZEL:-false} -> coverage.txt - -if [ "${USE_BAZEL}" = true ]; then - # TODO(yugui) Support bazel - exit 0 -fi - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic $d - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 00000000000..3d3011103d3 --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,20 @@ +version: v1 +plugins: + - remote: buf.build/library/plugins/go:v1.27.1-1 + out: . + opt: + - paths=source_relative + - remote: buf.build/library/plugins/go-grpc:v1.1.0-2 + out: . + opt: + - paths=source_relative + - require_unimplemented_servers=false + - name: grpc-gateway + out: . + opt: + - paths=source_relative + - allow_repeated_fields_in_body=true + - name: openapiv2 + out: . + opt: + - allow_repeated_fields_in_body=true diff --git a/buf.lock b/buf.lock new file mode 100644 index 00000000000..feb8c62fd64 --- /dev/null +++ b/buf.lock @@ -0,0 +1,7 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: googleapis + repository: googleapis + commit: 62f35d8aed1149c291d606d958a7ce32 diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 00000000000..86b1da1c507 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,179 @@ +version: v1 +name: buf.build/grpc-ecosystem/grpc-gateway +deps: + - buf.build/googleapis/googleapis +breaking: + use: + - FILE +lint: + use: + - DEFAULT + ignore_only: + DIRECTORY_SAME_PACKAGE: + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/echo_service.proto + - examples/internal/proto/examplepb/flow_combination.proto + - examples/internal/proto/examplepb/generate_unbound_methods.proto + - examples/internal/proto/examplepb/generated_input.proto + - examples/internal/proto/examplepb/non_standard_names.proto + - examples/internal/proto/examplepb/openapi_merge_a.proto + - examples/internal/proto/examplepb/openapi_merge_b.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/examplepb/stream.proto + - examples/internal/proto/examplepb/unannotated_echo_service.proto + - examples/internal/proto/examplepb/visibility_rule_echo_service.proto + - examples/internal/proto/examplepb/use_go_template.proto + - examples/internal/proto/examplepb/wrappers.proto + ENUM_VALUE_PREFIX: + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/pathenum/path_enum.proto + - protoc-gen-openapiv2/options/openapiv2.proto + - runtime/internal/examplepb/example.proto + - runtime/internal/examplepb/proto3.proto + ENUM_ZERO_VALUE_SUFFIX: + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/pathenum/path_enum.proto + - protoc-gen-openapiv2/options/openapiv2.proto + - runtime/internal/examplepb/example.proto + - runtime/internal/examplepb/proto3.proto + FIELD_LOWER_SNAKE_CASE: + - examples/internal/helloworld/helloworld.proto + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/non_standard_names.proto + - runtime/internal/examplepb/example.proto + - runtime/internal/examplepb/non_standard_names.proto + PACKAGE_DIRECTORY_MATCH: + - examples/internal/helloworld/helloworld.proto + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/echo_service.proto + - examples/internal/proto/examplepb/flow_combination.proto + - examples/internal/proto/examplepb/generate_unbound_methods.proto + - examples/internal/proto/examplepb/generated_input.proto + - examples/internal/proto/examplepb/non_standard_names.proto + - examples/internal/proto/examplepb/openapi_merge_a.proto + - examples/internal/proto/examplepb/openapi_merge_b.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/examplepb/stream.proto + - examples/internal/proto/examplepb/unannotated_echo_service.proto + - examples/internal/proto/examplepb/visibility_rule_echo_service.proto + - examples/internal/proto/examplepb/use_go_template.proto + - examples/internal/proto/examplepb/wrappers.proto + - examples/internal/proto/oneofenum/oneof_enum.proto + - examples/internal/proto/pathenum/path_enum.proto + - examples/internal/proto/sub/message.proto + - examples/internal/proto/sub2/message.proto + - internal/descriptor/apiconfig/apiconfig.proto + - internal/descriptor/openapiconfig/openapiconfig.proto + - protoc-gen-openapiv2/options/annotations.proto + - protoc-gen-openapiv2/options/openapiv2.proto + - runtime/internal/examplepb/example.proto + - runtime/internal/examplepb/non_standard_names.proto + - runtime/internal/examplepb/proto2.proto + - runtime/internal/examplepb/proto3.proto + PACKAGE_SAME_GO_PACKAGE: + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/echo_service.proto + - examples/internal/proto/examplepb/flow_combination.proto + - examples/internal/proto/examplepb/generate_unbound_methods.proto + - examples/internal/proto/examplepb/generated_input.proto + - examples/internal/proto/examplepb/non_standard_names.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/examplepb/stream.proto + - examples/internal/proto/examplepb/unannotated_echo_service.proto + - examples/internal/proto/examplepb/visibility_rule_echo_service.proto + - examples/internal/proto/examplepb/use_go_template.proto + - examples/internal/proto/examplepb/wrappers.proto + - runtime/internal/examplepb/example.proto + - runtime/internal/examplepb/non_standard_names.proto + - runtime/internal/examplepb/proto2.proto + - runtime/internal/examplepb/proto3.proto + PACKAGE_VERSION_SUFFIX: + - examples/internal/helloworld/helloworld.proto + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/echo_service.proto + - examples/internal/proto/examplepb/flow_combination.proto + - examples/internal/proto/examplepb/generate_unbound_methods.proto + - examples/internal/proto/examplepb/generated_input.proto + - examples/internal/proto/examplepb/non_standard_names.proto + - examples/internal/proto/examplepb/openapi_merge_a.proto + - examples/internal/proto/examplepb/openapi_merge_b.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/examplepb/stream.proto + - examples/internal/proto/examplepb/unannotated_echo_service.proto + - examples/internal/proto/examplepb/visibility_rule_echo_service.proto + - examples/internal/proto/examplepb/use_go_template.proto + - examples/internal/proto/examplepb/wrappers.proto + - examples/internal/proto/oneofenum/oneof_enum.proto + - examples/internal/proto/pathenum/path_enum.proto + - examples/internal/proto/sub/message.proto + - examples/internal/proto/sub2/message.proto + - internal/descriptor/apiconfig/apiconfig.proto + - internal/descriptor/openapiconfig/openapiconfig.proto + - protoc-gen-openapiv2/options/annotations.proto + - protoc-gen-openapiv2/options/openapiv2.proto + - runtime/internal/examplepb/example.proto + - runtime/internal/examplepb/non_standard_names.proto + - runtime/internal/examplepb/proto2.proto + - runtime/internal/examplepb/proto3.proto + RPC_REQUEST_RESPONSE_UNIQUE: + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/echo_service.proto + - examples/internal/proto/examplepb/flow_combination.proto + - examples/internal/proto/examplepb/generate_unbound_methods.proto + - examples/internal/proto/examplepb/generated_input.proto + - examples/internal/proto/examplepb/openapi_merge_a.proto + - examples/internal/proto/examplepb/openapi_merge_b.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/examplepb/stream.proto + - examples/internal/proto/examplepb/unannotated_echo_service.proto + - examples/internal/proto/examplepb/visibility_rule_echo_service.proto + - examples/internal/proto/examplepb/wrappers.proto + RPC_REQUEST_STANDARD_NAME: + - examples/internal/helloworld/helloworld.proto + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/echo_service.proto + - examples/internal/proto/examplepb/flow_combination.proto + - examples/internal/proto/examplepb/generate_unbound_methods.proto + - examples/internal/proto/examplepb/generated_input.proto + - examples/internal/proto/examplepb/non_standard_names.proto + - examples/internal/proto/examplepb/openapi_merge_a.proto + - examples/internal/proto/examplepb/openapi_merge_b.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/examplepb/stream.proto + - examples/internal/proto/examplepb/unannotated_echo_service.proto + - examples/internal/proto/examplepb/visibility_rule_echo_service.proto + - examples/internal/proto/examplepb/wrappers.proto + - runtime/internal/examplepb/non_standard_names.proto + RPC_RESPONSE_STANDARD_NAME: + - examples/internal/helloworld/helloworld.proto + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/echo_service.proto + - examples/internal/proto/examplepb/flow_combination.proto + - examples/internal/proto/examplepb/generate_unbound_methods.proto + - examples/internal/proto/examplepb/generated_input.proto + - examples/internal/proto/examplepb/non_standard_names.proto + - examples/internal/proto/examplepb/openapi_merge_a.proto + - examples/internal/proto/examplepb/openapi_merge_b.proto + - examples/internal/proto/examplepb/response_body_service.proto + - examples/internal/proto/examplepb/stream.proto + - examples/internal/proto/examplepb/unannotated_echo_service.proto + - examples/internal/proto/examplepb/visibility_rule_echo_service.proto + - examples/internal/proto/examplepb/use_go_template.proto + - examples/internal/proto/examplepb/wrappers.proto + - runtime/internal/examplepb/non_standard_names.proto + SERVICE_PASCAL_CASE: + - examples/internal/proto/examplepb/a_bit_of_everything.proto + SERVICE_SUFFIX: + - examples/internal/helloworld/helloworld.proto + - examples/internal/proto/examplepb/a_bit_of_everything.proto + - examples/internal/proto/examplepb/flow_combination.proto + - examples/internal/proto/examplepb/openapi_merge_a.proto + - examples/internal/proto/examplepb/openapi_merge_b.proto + allow_comment_ignores: true +# Note: the build configuration goes last in this +# files so we can append excludes at push time. +build: + excludes: + - bazel-grpc-gateway diff --git a/codegenerator/BUILD.bazel b/codegenerator/BUILD.bazel deleted file mode 100644 index 1eddaaf7395..00000000000 --- a/codegenerator/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -package(default_visibility = ["//:generators"]) - -go_library( - name = "go_default_library", - srcs = [ - "doc.go", - "parse_req.go", - ], - importpath = "github.com/grpc-ecosystem/grpc-gateway/codegenerator", - deps = [ - "@com_github_golang_protobuf//proto:go_default_library", - "@com_github_golang_protobuf//protoc-gen-go/plugin:go_default_library", - ], -) - -go_test( - name = "go_default_xtest", - srcs = ["parse_req_test.go"], - deps = [ - ":go_default_library", - "@com_github_golang_protobuf//proto:go_default_library", - "@com_github_golang_protobuf//protoc-gen-go/plugin:go_default_library", - ], -) diff --git a/codegenerator/parse_req.go b/codegenerator/parse_req.go deleted file mode 100644 index e74575bddcd..00000000000 --- a/codegenerator/parse_req.go +++ /dev/null @@ -1,23 +0,0 @@ -package codegenerator - -import ( - "fmt" - "io" - "io/ioutil" - - "github.com/golang/protobuf/proto" - plugin "github.com/golang/protobuf/protoc-gen-go/plugin" -) - -// ParseRequest parses a code generator request from a proto Message. -func ParseRequest(r io.Reader) (*plugin.CodeGeneratorRequest, error) { - input, err := ioutil.ReadAll(r) - if err != nil { - return nil, fmt.Errorf("failed to read code generator request: %v", err) - } - req := new(plugin.CodeGeneratorRequest) - if err = proto.Unmarshal(input, req); err != nil { - return nil, fmt.Errorf("failed to unmarshal code generator request: %v", err) - } - return req, nil -} diff --git a/docs/.gitignore b/docs/.gitignore index ca35be08d44..16bc55f6ae3 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1,6 @@ +*.gem +.bundle +.ruby-version +.jekyll-cache +.sass-cache _site diff --git a/docs/Gemfile b/docs/Gemfile index 5e3964ff666..c1dafd9b31c 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -1,9 +1,11 @@ -source 'https://rubygems.org' - -group :development, :test do - gem "github-pages", group: :jekyll_plugins - gem 'jekyll', '~> 3.7.0' - gem 'jekyll-redirect-from', '~> 0.13.0' - gem 'jekyll-sitemap', '~> 1.2.0' - gem 'jekyll-toc', '~> 0.5.1' +source "https://rubygems.org" +gem "just-the-docs" +group :jekyll_plugins do + gem "github-pages" # GitHub Pages + gem "jekyll-optional-front-matter" # GitHub Pages + gem "jekyll-default-layout" # GitHub Pages + gem "jekyll-titles-from-headings" # GitHub Pages + gem "jekyll-readme-index" # GitHub Pages + gem "jekyll-relative-links" # GitHub Pages + gem 'jekyll-include-cache' # GitHub Pages end diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 6bc4cb1f8f7..c07eb8c57b9 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,256 +1,179 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.2.9) + activesupport (4.2.7) i18n (~> 0.7) + json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) + addressable (2.4.0) coffee-script (2.4.1) coffee-script-source execjs coffee-script-source (1.11.1) colorator (1.1.0) - commonmarker (0.17.9) - ruby-enum (~> 0.5) - concurrent-ruby (1.0.5) - em-websocket (0.5.1) - eventmachine (>= 0.12.9) - http_parser.rb (~> 0.6.0) - ethon (0.11.0) - ffi (>= 1.3.0) - eventmachine (1.2.5) - execjs (2.7.0) - faraday (0.14.0) - multipart-post (>= 1.2, < 3) - ffi (1.9.23) + concurrent-ruby (1.1.10) + ethon (0.15.0) + ffi (>= 1.15.0) + execjs (2.8.1) + faraday (2.6.0) + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.1) + ffi (1.15.5) forwardable-extended (2.6.0) - gemoji (3.0.0) - github-pages (182) - activesupport (= 4.2.9) - github-pages-health-check (= 1.4.0) - jekyll (= 3.7.3) - jekyll-avatar (= 0.5.0) - jekyll-coffeescript (= 1.1.1) - jekyll-commonmark-ghpages (= 0.1.5) - jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.9.3) - jekyll-gist (= 1.5.0) - jekyll-github-metadata (= 2.9.4) - jekyll-mentions (= 1.3.0) - jekyll-optional-front-matter (= 0.3.0) + gemoji (2.1.0) + github-pages (105) + activesupport (= 4.2.7) + github-pages-health-check (= 1.2.0) + jekyll (= 3.3.1) + jekyll-avatar (= 0.4.2) + jekyll-coffeescript (= 1.0.1) + jekyll-feed (= 0.8.0) + jekyll-gist (= 1.4.0) + jekyll-github-metadata (= 2.2.0) + jekyll-mentions (= 1.2.0) jekyll-paginate (= 1.1.0) - jekyll-readme-index (= 0.2.0) - jekyll-redirect-from (= 0.13.0) - jekyll-relative-links (= 0.5.3) - jekyll-remote-theme (= 0.2.3) - jekyll-sass-converter (= 1.5.2) - jekyll-seo-tag (= 2.4.0) - jekyll-sitemap (= 1.2.0) + jekyll-redirect-from (= 0.11.0) + jekyll-sass-converter (= 1.3.0) + jekyll-seo-tag (= 2.1.0) + jekyll-sitemap (= 0.12.0) jekyll-swiss (= 0.4.0) - jekyll-theme-architect (= 0.1.1) - jekyll-theme-cayman (= 0.1.1) - jekyll-theme-dinky (= 0.1.1) - jekyll-theme-hacker (= 0.1.1) - jekyll-theme-leap-day (= 0.1.1) - jekyll-theme-merlot (= 0.1.1) - jekyll-theme-midnight (= 0.1.1) - jekyll-theme-minimal (= 0.1.1) - jekyll-theme-modernist (= 0.1.1) - jekyll-theme-primer (= 0.5.3) - jekyll-theme-slate (= 0.1.1) - jekyll-theme-tactile (= 0.1.1) - jekyll-theme-time-machine (= 0.1.1) - jekyll-titles-from-headings (= 0.5.1) - jemoji (= 0.9.0) - kramdown (= 1.16.2) - liquid (= 4.0.0) - listen (= 3.1.5) + jemoji (= 0.7.0) + kramdown (= 1.11.1) + liquid (= 3.0.6) + listen (= 3.0.6) mercenary (~> 0.3) - minima (= 2.4.1) - nokogiri (>= 1.8.1, < 2.0) - rouge (= 2.2.1) + minima (= 2.0.0) + rouge (= 1.11.1) terminal-table (~> 1.4) - github-pages-health-check (1.4.0) + github-pages-health-check (1.2.0) addressable (~> 2.3) net-dns (~> 0.8) octokit (~> 4.0) - public_suffix (~> 2.0) - typhoeus (~> 1.3) - html-pipeline (2.7.1) + public_suffix (~> 1.4) + typhoeus (~> 0.7) + html-pipeline (2.14.2) activesupport (>= 2) nokogiri (>= 1.4) - http_parser.rb (0.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) - jekyll (3.7.3) + jekyll (3.3.1) addressable (~> 2.4) colorator (~> 1.0) - em-websocket (~> 0.5) - i18n (~> 0.7) jekyll-sass-converter (~> 1.0) - jekyll-watch (~> 2.0) - kramdown (~> 1.14) - liquid (~> 4.0) + jekyll-watch (~> 1.1) + kramdown (~> 1.3) + liquid (~> 3.0) mercenary (~> 0.3.3) pathutil (~> 0.9) - rouge (>= 1.7, < 4) + rouge (~> 1.7) safe_yaml (~> 1.0) - jekyll-avatar (0.5.0) + jekyll-avatar (0.4.2) jekyll (~> 3.0) - jekyll-coffeescript (1.1.1) + jekyll-coffeescript (1.0.1) coffee-script (~> 2.2) - coffee-script-source (~> 1.11.1) - jekyll-commonmark (1.2.0) - commonmarker (~> 0.14) - jekyll (>= 3.0, < 4.0) - jekyll-commonmark-ghpages (0.1.5) - commonmarker (~> 0.17.6) - jekyll-commonmark (~> 1) - rouge (~> 2) - jekyll-default-layout (0.1.4) - jekyll (~> 3.0) - jekyll-feed (0.9.3) + jekyll-default-layout (0.1.5) + jekyll (>= 3.0, < 5.0) + jekyll-feed (0.8.0) jekyll (~> 3.3) - jekyll-gist (1.5.0) + jekyll-gist (1.4.0) octokit (~> 4.2) - jekyll-github-metadata (2.9.4) + jekyll-github-metadata (2.2.0) jekyll (~> 3.1) octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.3.0) + jekyll-include-cache (0.1.0) + jekyll (~> 3.3) + jekyll-mentions (1.2.0) activesupport (~> 4.0) html-pipeline (~> 2.3) jekyll (~> 3.0) - jekyll-optional-front-matter (0.3.0) - jekyll (~> 3.0) + jekyll-optional-front-matter (0.3.2) + jekyll (>= 3.0, < 5.0) jekyll-paginate (1.1.0) - jekyll-readme-index (0.2.0) - jekyll (~> 3.0) - jekyll-redirect-from (0.13.0) - jekyll (~> 3.3) - jekyll-relative-links (0.5.3) - jekyll (~> 3.3) - jekyll-remote-theme (0.2.3) - jekyll (~> 3.5) - rubyzip (>= 1.2.1, < 3.0) - typhoeus (>= 0.7, < 2.0) - jekyll-sass-converter (1.5.2) - sass (~> 3.4) - jekyll-seo-tag (2.4.0) + jekyll-readme-index (0.3.0) + jekyll (>= 3.0, < 5.0) + jekyll-redirect-from (0.11.0) + jekyll (>= 2.0) + jekyll-relative-links (0.6.1) + jekyll (>= 3.3, < 5.0) + jekyll-sass-converter (1.3.0) + sass (~> 3.2) + jekyll-seo-tag (2.1.0) jekyll (~> 3.3) - jekyll-sitemap (1.2.0) + jekyll-sitemap (0.12.0) jekyll (~> 3.3) jekyll-swiss (0.4.0) - jekyll-theme-architect (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-cayman (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-dinky (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-leap-day (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-merlot (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-midnight (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-minimal (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-modernist (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-primer (0.5.3) - jekyll (~> 3.5) - jekyll-github-metadata (~> 2.9) - jekyll-seo-tag (~> 2.0) - jekyll-theme-slate (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-tactile (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-theme-time-machine (0.1.1) - jekyll (~> 3.5) - jekyll-seo-tag (~> 2.0) - jekyll-titles-from-headings (0.5.1) - jekyll (~> 3.3) - jekyll-toc (0.5.2) - nokogiri (~> 1.6) - jekyll-watch (2.0.0) + jekyll-titles-from-headings (0.5.3) + jekyll (>= 3.3, < 5.0) + jekyll-watch (1.5.1) listen (~> 3.0) - jemoji (0.9.0) - activesupport (~> 4.0, >= 4.2.9) - gemoji (~> 3.0) + jemoji (0.7.0) + activesupport (~> 4.0) + gemoji (~> 2.0) html-pipeline (~> 2.2) - jekyll (~> 3.0) - kramdown (1.16.2) - liquid (4.0.0) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) + jekyll (>= 3.0) + json (1.8.6) + just-the-docs (0.1.6) + jekyll (~> 3.3) + rake (~> 10.0) + kramdown (1.11.1) + liquid (3.0.6) + listen (3.0.6) + rb-fsevent (>= 0.9.3) + rb-inotify (>= 0.9.7) mercenary (0.3.6) - mini_portile2 (2.3.0) - minima (2.4.1) - jekyll (~> 3.5) - jekyll-feed (~> 0.9) - jekyll-seo-tag (~> 2.1) - minitest (5.11.3) - multipart-post (2.0.0) - net-dns (0.8.0) - nokogiri (1.8.2) - mini_portile2 (~> 2.3.0) - octokit (4.8.0) - sawyer (~> 0.8.0, >= 0.5.3) - pathutil (0.16.1) + mini_portile2 (2.8.0) + minima (2.0.0) + minitest (5.16.3) + net-dns (0.9.0) + nokogiri (1.13.9) + mini_portile2 (~> 2.8.0) + racc (~> 1.4) + octokit (4.25.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) + pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (2.0.5) - rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - rouge (2.2.1) - ruby-enum (0.7.2) - i18n - ruby_dep (1.5.0) - rubyzip (1.2.1) - safe_yaml (1.0.4) - sass (3.5.6) + public_suffix (1.5.3) + racc (1.6.0) + rake (10.5.0) + rb-fsevent (0.11.2) + rb-inotify (0.10.1) + ffi (~> 1.0) + rouge (1.11.1) + ruby2_keywords (0.0.5) + safe_yaml (1.0.5) + sass (3.7.4) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.8.1) - addressable (>= 2.3.5, < 2.6) - faraday (~> 0.8, < 1.0) + sawyer (0.9.2) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) thread_safe (0.3.6) - typhoeus (1.3.0) - ethon (>= 0.9.0) - tzinfo (1.2.5) + typhoeus (0.8.0) + ethon (>= 0.8.0) + tzinfo (1.2.10) thread_safe (~> 0.1) - unicode-display_width (1.3.0) + unicode-display_width (1.8.0) PLATFORMS ruby DEPENDENCIES github-pages - jekyll (~> 3.7.0) - jekyll-redirect-from (~> 0.13.0) - jekyll-sitemap (~> 1.2.0) - jekyll-toc (~> 0.5.1) + jekyll-default-layout + jekyll-include-cache + jekyll-optional-front-matter + jekyll-readme-index + jekyll-relative-links + jekyll-titles-from-headings + just-the-docs BUNDLED WITH - 1.15.4 + 2.1.4 diff --git a/docs/_config.yaml b/docs/_config.yaml deleted file mode 100644 index 48031995698..00000000000 --- a/docs/_config.yaml +++ /dev/null @@ -1,19 +0,0 @@ -theme: jekyll-theme-architect - -repository: grpc-ecosystem/grpc-gateway - -collections: - docs: - output: true - -defaults: - - scope: - path: "" - values: - layout: "default" - -plugins: - - jekyll-toc - -exclude: - - run.sh diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000000..b24108b0a14 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,114 @@ +# Site settings +# These are used to personalize your new site. If you look in the HTML files, +# you will see them accessed via {{ site.title }}, {{ site.github_repo }}, and so on. +# You can create any custom variable you would like, and they will be accessible +# in the templates via {{ site.myvariable }}. +title: gRPC-Gateway +description: gRPC-Gateway Documentation Website +baseurl: "/grpc-gateway" # the subpath of your site, e.g. /blog +url: "https://grpc-ecosystem.github.io" # the base hostname & protocol for your site, e.g. http://example.com + +repository: grpc-ecosystem/grpc-gateway +remote_theme: pmarsceill/just-the-docs + +permalink: pretty +exclude: ["run.sh"] + +# Set a path/url to a logo that will be displayed instead of the title +#logo: "/assets/images/grpc-gateway.png" + +# Enable or disable the site search +# Supports true (default) or false +search_enabled: true +search: + # Split pages into sections that can be searched individually + # Supports 1 - 6, default: 2 + heading_level: 2 + # Maximum amount of previews per search result + # Default: 3 + previews: 2 + # Maximum amount of words to display before a matched word in the preview + # Default: 5 + preview_words_before: 3 + # Maximum amount of words to display after a matched word in the preview + # Default: 10 + preview_words_after: 3 + # Set the search token separator + # Default: /[\s\-/]+/ + # Example: enable support for hyphenated search words + tokenizer_separator: /[\s/]+/ + # Display the relative url in search results + # Supports true (default) or false + rel_url: true + # Enable or disable the search button that appears in the bottom right corner of every page + # Supports true or false (default) + button: false + +# Enable or disable heading anchors +heading_anchors: true + +# Aux links for the upper right navigation +aux_links: + "gRPC-Gateway on GitHub": + - "https://github.com/grpc-ecosystem/grpc-gateway" + +# Makes Aux links open in a new tab. Default is false +aux_links_new_tab: false + +# Sort order for navigation links +# nav_sort: case_insensitive # default, equivalent to nil +nav_sort: case_sensitive # Capital letters sorted before lowercase + +# Footer content +# appears at the bottom of every page's main content + +# Back to top link +back_to_top: true +back_to_top_text: "Back to top" + +footer_content: 'Copyright © the gRPC-Gateway Authors. Distributed by a BSD 3-Clause License.' + +# Footer last edited timestamp +last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter +last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html + +# Footer "Edit this page on GitHub" link text +gh_edit_link: true # show or hide edit this page link +gh_edit_link_text: "Edit this page on GitHub" +gh_edit_repository: "https://github.com/grpc-ecosystem/grpc-gateway" # the github URL for your repo +gh_edit_branch: "master" # the branch that your docs is served from +gh_edit_source: docs # the source that your files originate from +gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately + +# Color scheme currently only supports "dark", "light"/nil (default), or a custom scheme that you define +color_scheme: nil + +# Disqus Comments +# disqus: +# Leave shortname blank to disable comments site-wide. +# Enable comments for any post by adding `comments: true` to that post's YAML Front Matter. +# shortname: + +# Google Analytics Tracking +# e.g, UA-1234567-89 +# ga_tracking: +# ga_tracking_anonymize_ip: true # Use GDPR compliant Google Analytics settings (true/nil by default) + +plugins: + - jekyll-seo-tag + - jekyll-include-cache + +kramdown: + syntax_highlighter_opts: + block: + line_numbers: false + +compress_html: + clippings: all + comments: all + endings: all + startings: [] + blanklines: false + profile: false + # ignore: + # envs: all diff --git a/docs/_docs/background.md b/docs/_docs/background.md deleted file mode 100644 index 63463a1ba0f..00000000000 --- a/docs/_docs/background.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -category: documentation ---- - -# Background - -gRPC is great -- it generates API clients and server stubs in many programming languages, it is fast, easy-to-use, bandwidth-efficient and its design is combat-proven by Google. -However, you might still want to provide a traditional RESTful API as well. Reasons can range from maintaining backwards-compatibility, supporting languages or clients not well supported by gRPC to simply maintaining the aesthetics and tooling involved with a RESTful architecture. - -This project aims to provide that HTTP+JSON interface to your gRPC service. A small amount of configuration in your service to attach HTTP semantics is all that's needed to generate a reverse-proxy with this library. - diff --git a/docs/_docs/customizingyourgateway.md b/docs/_docs/customizingyourgateway.md deleted file mode 100644 index 758611f2700..00000000000 --- a/docs/_docs/customizingyourgateway.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: Customizing your gateway -category: documentation -order: 101 ---- - -# Customizing your gateway - -## Message serialization - -You might want to serialize request/response messages in MessagePack instead of JSON, for example. - -1. Write a custom implementation of [`Marshaler`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#Marshaler) -2. Register your marshaler with [`WithMarshalerOption`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#WithMarshalerOption) - e.g. - ```go - var m your.MsgPackMarshaler - mux := runtime.NewServeMux(runtime.WithMarshalerOption("application/x-msgpack", m)) - ``` - -You can see [the default implementation for JSON](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/marshal_jsonpb.go) for reference. - -## Mapping from HTTP request headers to gRPC client metadata -You might not like [the default mapping rule](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#DefaultHeaderMatcher) and might want to pass through all the HTTP headers, for example. - -1. Write a [`HeaderMatcherFunc`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#HeaderMatcherFunc). -2. Register the function with [`WithIncomingHeaderMatcher`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#WithIncomingHeaderMatcher) - - e.g. - ```go - func yourMatcher(headerName string) (mdName string, ok bool) { - ... - } - ... - mux := runtime.NewServeMux(runtime.WithIncomingHeaderMatcher(yourMatcher)) - - ``` - -## Mapping from gRPC server metadata to HTTP response headers -ditto. Use [`WithOutgoingHeaderMatcher`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#WithOutgoingHeaderMatcher) - -## Mutate response messages or set response headers -You might want to return a subset of response fields as HTTP response headers; -You might want to simply set an application-specific token in a header. -Or you might want to mutate the response messages to be returned. - -1. Write a filter function. - ```go - func myFilter(ctx context.Context, w http.ResponseWriter, resp proto.Message) error { - w.Header().Set("X-My-Tracking-Token", resp.Token) - resp.Token = "" - return nil - } - ``` -2. Register the filter with [`WithForwardResponseOption`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#WithForwardResponseOption) - - e.g. - ```go - mux := runtime.NewServeMux(runtime.WithForwardResponseOption(myFilter)) - ``` - -## Error handler -http://mycodesmells.com/post/grpc-gateway-error-handler - -## Replace a response forwarder per method -You might want to keep the behavior of the current marshaler but change only a message forwarding of a certain API method. - -1. write a custom forwarder which is compatible to [`ForwardResponseMessage`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#ForwardResponseMessage) or [`ForwardResponseStream`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#ForwardResponseStream). -2. replace the default forwarder of the method with your one. - - e.g. add `forwarder_overwrite.go` into the go package of the generated code, - ```go - package generated - - import ( - "net/http" - - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/golang/protobuf/proto" - "golang.org/x/net/context" - ) - - func forwardCheckoutResp(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, req *http.Request, resp proto.Message, opts ...func(context.Context, http.ResponseWriter, proto.Message) error) { - if someCondition(resp) { - http.Error(w, "not enough credit", http. StatusPaymentRequired) - return - } - runtime.ForwardResponseMessage(ctx, mux, marshaler, w, req, resp, opts...) - } - - func init() { - forward_MyService_Checkout_0 = forwardCheckoutResp - } - ``` diff --git a/docs/_docs/examples.md b/docs/_docs/examples.md deleted file mode 100644 index 78d93d720db..00000000000 --- a/docs/_docs/examples.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -category: documentation ---- - -# Examples - -Examples are available under `examples` directory. -* `proto/examplepb/echo_service.proto`, `proto/examplepb/a_bit_of_everything.proto`, `proto/examplepb/unannotated_echo_service.proto`: service definition - * `proto/examplepb/echo_service.pb.go`, `proto/examplepb/a_bit_of_everything.pb.go`, `proto/examplepb/unannotated_echo_service.pb.go`: [generated] stub of the service - * `proto/examplepb/echo_service.pb.gw.go`, `proto/examplepb/a_bit_of_everything.pb.gw.go`, `proto/examplepb/uannotated_echo_service.pb.gw.go`: [generated] reverse proxy for the service - * `proto/examplepb/unannotated_echo_service.yaml`: gRPC API Configuration for ```unannotated_echo_service.proto``` -* `server/main.go`: service implementation -* `main.go`: entrypoint of the generated reverse proxy - -To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), gRPC-gateway, and a gRPC server, see [this code example by CoreOS](https://github.com/philips/grpc-gateway-example/blob/master/cmd/serve.go) (and its accompanying [blog post](https://coreos.com/blog/gRPC-protobufs-swagger.html)) - - diff --git a/docs/_docs/faq.md b/docs/_docs/faq.md deleted file mode 100644 index 766c402c80b..00000000000 --- a/docs/_docs/faq.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -category: documentation -title: FAQ ---- - -# FAQ - -## How can I write the annotations which grpc-gateway requires? -Grpc-gateway follows the spec of [`google.api.HttpRule`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto). -So first check out the documentation if it is feasible in the spec. - -See also [a past discussion](https://groups.google.com/d/msg/grpc-io/Xqx80hG0D44/VNCDHjeE6pUJ) in grpc-io mailing list. - -## I want to support a certain style of HTTP request but the code generated by grpc-gateway does not. How can I support this style? -See the question above at first. - -Grpc-gateway is intended to cover 80% of use cases without forcing you to write comprehensive but complicated annotations. So grpc-gateway itself does not always cover all the use cases you have by design. In other words, grpc-gateway automates typical boring boilerplate mapping between gRPC and HTTP/1 communication, but it does not do arbitrarily complex custom mappings for you. - -On the other hand, you can still add whatever you want as a middleware which wraps [`runtime.ServeMux`](http://godoc.org/github.com/grpc-ecosystem/grpc-gateway/runtime#ServeMux). Since `runtime.ServeMux` is just a standard [`http.Handler`](http://golang.org/pkg/http#Handler), you can easily write a custom wrapper of `runtime.ServeMux`, leveraged with existing third-party libraries in Go. -e.g. https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/main.go - -## My gRPC server is written in (Scala|C++|Ruby|Haskell|....). Is there a (Scala|C++|Ruby|Haskell|....) version of grpc-gateway? - -AFAIK, no. But it should not be a big issue because the reverse-proxy which grpc-gateway generates usually works as an independent process and communicates with your gRPC server over TCP or a unix-domain socket. - -## Why are the models in the swagger specification prefixed with the last part of the proto package name? - -The reason to generate the prefixes is that we don't have a guaranteed unique namespace. If two packages produce different Foo messages then we will have trouble. - -## Why not strip the prefix? - -When a message is added which happens to conflict with another message (e.g. by importing a message with the same name from a different package) it will break code that is very far away from the code that changed. This is in an effort to adhere to the [principle of least astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment). diff --git a/docs/_docs/features.md b/docs/_docs/features.md deleted file mode 100644 index 0bae3608cbb..00000000000 --- a/docs/_docs/features.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -category: documentation ---- - -# Features - -## Supported -* Generating JSON API handlers -* Method parameters in request body -* Method parameters in request path -* Method parameters in query string -* Enum fields in path parameter (including repeated enum fields). -* Mapping streaming APIs to newline-delimited JSON streams -* Mapping HTTP headers with `Grpc-Metadata-` prefix to gRPC metadata (prefixed with `grpcgateway-`) -* Optionally emitting API definition for [Swagger](http://swagger.io). -* Setting [gRPC timeouts](http://www.grpc.io/docs/guides/wire.html) through inbound HTTP `Grpc-Timeout` header. -* Partial support for [gRPC API Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) files as an alternative to annotation. - -## Want to support -But not yet. -* Optionally generating the entrypoint. #8 -* `import_path` parameter - -## No plan to support -But patch is welcome. -* Method parameters in HTTP headers -* Handling trailer metadata -* Encoding request/response body in XML -* True bi-directional streaming. (Probably impossible?) - diff --git a/docs/_docs/grpcapiconfiguration.md b/docs/_docs/grpcapiconfiguration.md deleted file mode 100644 index 6547e7d4462..00000000000 --- a/docs/_docs/grpcapiconfiguration.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: Usage without annotations (gRPC API Configuration) -category: documentation -order: 100 ---- - -# gRPC API Configuration -In some sitations annotating the .proto file of a service is not an option. For example you might not have control over the .proto file or you might want to expose the same gRPC API multiple times in completely different ways. - -Google Cloud Platform offers a way to do this for services hosted with them called ["gRPC API Configuration"](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config). It can be used to define the behavior of a gRPC API service without modifications to the service itself in the form of [YAML](https://en.wikipedia.org/wiki/YAML) configuration files. - -grpc-gateway generators implement the [HTTP rules part](https://cloud.google.com/endpoints/docs/grpc-service-config/reference/rpc/google.api#httprule) of this specification. This allows you to take a completely unannotated service proto file, add a YAML file describing its HTTP endpoints and use them together like a annotated proto file with the grpc-gateway generators. - -## Usage of gRPC API Configuration YAML files -The following is equivalent to the basic [usage example](usage.html) but without direct annotation for grpc-gateway in the .proto file. Only some steps require minor changes to use a gRPC API Configuration YAML file instead: - -1. Define your service in gRPC as usual - - your_service.proto: - ```protobuf - syntax = "proto3"; - package example; - message StringMessage { - string value = 1; - } - - service YourService { - rpc Echo(StringMessage) returns (StringMessage) {} - } - ``` - -2. Instead of annotating the .proto file in this step leave it untouched and create a `your_service.yaml` with the following content: - ```yaml - type: google.api.Service - config_version: 3 - - http: - rules: - - selector: example.YourService.Echo - post: /v1/example/echo - body: "*" - ``` - Use a [linter](http://www.yamllint.com/) to validate your YAML. - -3. Generate gRPC stub as before - - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --go_out=plugins=grpc:. \ - path/to/your_service.proto - ``` - - It will generate a stub file `path/to/your_service.pb.go`. -4. Implement your service in gRPC as usual - 1. (Optional) Generate gRPC stub in the language you want. - - e.g. - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --ruby_out=. \ - path/to/your/service_proto - - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --plugin=protoc-gen-grpc=grpc_ruby_plugin \ - --grpc-ruby_out=. \ - path/to/your/service.proto - ``` - 2. Add the googleapis-common-protos gem (or your language equivalent) as a dependency to your project. - 3. Implement your service - -5. Generate reverse-proxy. Here we have to pass the path to the `your_service.yaml` in addition to the .proto file: - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --grpc-gateway_out=logtostderr=true,grpc_api_configuration=path/to/your_service.yaml:. \ - path/to/your_service.proto - ``` - - This will generate a reverse proxy `path/to/your_service.pb.gw.go` that is identical to the one produced for the annotated proto. - - Note: After generating the code for each of the stubs, in order to build the code, you will want to run ```go get .``` from the directory containing the stubs. - -6. Write an entrypoint - - Now you need to write an entrypoint of the proxy server. This step is the same whether the file is annotated or not. - ```go - package main - - import ( - "flag" - "net/http" - - "github.com/golang/glog" - "golang.org/x/net/context" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "google.golang.org/grpc" - - gw "path/to/your_service_package" - ) - - var ( - echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of YourService") - ) - - func run() error { - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - mux := runtime.NewServeMux() - opts := []grpc.DialOption{grpc.WithInsecure()} - err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts) - if err != nil { - return err - } - - return http.ListenAndServe(":8080", mux) - } - - func main() { - flag.Parse() - defer glog.Flush() - - if err := run(); err != nil { - glog.Fatal(err) - } - } - ``` - -7. (Optional) Generate swagger definitions - -Swagger generation in this step is equivalent to gateway generation. Again pass the path to the yaml file in addition to the proto: - - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --swagger_out=logtostderr=true,grpc_api_configuration=path/to/your_service.yaml:. \ - path/to/your_service.proto - ``` - -All other steps work as before. If you want you can remove the googleapis include path in step 3 and 4 as the unannotated proto no longer requires them. diff --git a/docs/_docs/usage.md b/docs/_docs/usage.md deleted file mode 100644 index 8862c523273..00000000000 --- a/docs/_docs/usage.md +++ /dev/null @@ -1,194 +0,0 @@ ---- -category: documentation ---- - -# How do I use this? - -## Installation -First you need to install ProtocolBuffers 3.0.0-beta-3 or later. - -```sh -mkdir tmp -cd tmp -git clone https://github.com/google/protobuf -cd protobuf -./autogen.sh -./configure -make -make check -sudo make install -``` - -Then, `go get -u` as usual the following packages: - -```sh -go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway -go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger -go get -u github.com/golang/protobuf/protoc-gen-go -``` - -## Usage -Make sure that your `$GOPATH/bin` is in your `$PATH`. - -1. Define your service in gRPC - - your_service.proto: - ```protobuf - syntax = "proto3"; - package example; - message StringMessage { - string value = 1; - } - - service YourService { - rpc Echo(StringMessage) returns (StringMessage) {} - } - ``` -2. Add a [custom option](https://cloud.google.com/service-management/reference/rpc/google.api#http) to the .proto file - - your_service.proto: - ```diff - syntax = "proto3"; - package example; - + - +import "google/api/annotations.proto"; - + - message StringMessage { - string value = 1; - } - - service YourService { - - rpc Echo(StringMessage) returns (StringMessage) {} - + rpc Echo(StringMessage) returns (StringMessage) { - + option (google.api.http) = { - + post: "/v1/example/echo" - + body: "*" - + }; - + } - } - ``` - - If you do not want to modify the proto file for use with grpc-gateway you can alternatively use an external [gRPC API Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) file. [Check our documentation](grpcapiconfiguration.html) for more information. - -3. Generate gRPC stub - - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --go_out=plugins=grpc:. \ - path/to/your_service.proto - ``` - - It will generate a stub file `path/to/your_service.pb.go`. -4. Implement your service in gRPC as usual - 1. (Optional) Generate gRPC stub in the language you want. - - e.g. - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --ruby_out=. \ - path/to/your/service_proto - - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --plugin=protoc-gen-grpc=grpc_ruby_plugin \ - --grpc-ruby_out=. \ - path/to/your/service.proto - ``` - 2. Add the googleapis-common-protos gem (or your language equivalent) as a dependency to your project. - 3. Implement your service - -5. Generate reverse-proxy - - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --grpc-gateway_out=logtostderr=true:. \ - path/to/your_service.proto - ``` - - It will generate a reverse proxy `path/to/your_service.pb.gw.go`. - - Note: After generating the code for each of the stubs, in order to build the code, you will want to run ```go get .``` from the directory containing the stubs. - -6. Write an entrypoint - - Now you need to write an entrypoint of the proxy server. - ```go - package main - - import ( - "flag" - "net/http" - - "github.com/golang/glog" - "golang.org/x/net/context" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "google.golang.org/grpc" - - gw "path/to/your_service_package" - ) - - var ( - echoEndpoint = flag.String("echo_endpoint", "localhost:9090", "endpoint of YourService") - ) - - func run() error { - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - mux := runtime.NewServeMux() - opts := []grpc.DialOption{grpc.WithInsecure()} - err := gw.RegisterYourServiceHandlerFromEndpoint(ctx, mux, *echoEndpoint, opts) - if err != nil { - return err - } - - return http.ListenAndServe(":8080", mux) - } - - func main() { - flag.Parse() - defer glog.Flush() - - if err := run(); err != nil { - glog.Fatal(err) - } - } - ``` - -7. (Optional) Generate swagger definitions - - ```sh - protoc -I/usr/local/include -I. \ - -I$GOPATH/src \ - -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ - --swagger_out=logtostderr=true:. \ - path/to/your_service.proto - ``` - -## Parameters and flags -`protoc-gen-grpc-gateway` supports custom mapping from Protobuf `import` to Golang import path. -They are compatible to [the parameters with same names in `protoc-gen-go`](https://github.com/golang/protobuf#parameters). - -In addition we also support the `request_context` parameter in order to use the `http.Request`'s Context (only for Go 1.7 and above). -This parameter can be useful to pass request scoped context between the gateway and the gRPC service. - -`protoc-gen-grpc-gateway` also supports some more command line flags to control logging. You can give these flags together with parameters above. Run `protoc-gen-grpc-gateway --help` for more details about the flags. - -# Mapping gRPC to HTTP - -* [How gRPC error codes map to HTTP status codes in the response](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/errors.go#L15) -* HTTP request source IP is added as `X-Forwarded-For` gRPC request header -* HTTP request host is added as `X-Forwarded-Host` gRPC request header -* HTTP `Authorization` header is added as `authorization` gRPC request header -* Remaining Permanent HTTP header keys (as specified by the IANA [here](http://www.iana.org/assignments/message-headers/message-headers.xhtml) are prefixed with `grpcgateway-` and added with their values to gRPC request header -* HTTP headers that start with 'Grpc-Metadata-' are mapped to gRPC metadata (prefixed with `grpcgateway-`) -* While configurable, the default {un,}marshaling uses [jsonpb](https://godoc.org/github.com/golang/protobuf/jsonpb) with `OrigName: true`. - diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index 1e45e5fb013..837b3b644ec 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -1,92 +1,187 @@ - - - - - - - - - +--- +layout: table_wrappers +--- - + -{% seo %} - + +{% include head.html %} + + + + Link + + + + + + Search + + + + + + Menu + + + + + + Expand + + + + + + Document + + + + + - -
-
- -

{{ site.title | default: site.github.repository_name }}

-
-

{{ site.description | default: site.github.project_tagline }}

- {% if site.github.is_project_page %} - View project on GitHub + +
+
+ {% if site.search_enabled != false %} + + {% endif %} + {% if site.aux_links %} + + {% endif %} +
+
+ {% unless page.url == "/" %} + {% if page.parent %} + {% endif %} - {% if site.github.is_user_page %} - Follow me on GitHub + {% endunless %} +
+ {% if site.heading_anchors != false %} + {% include vendor/anchor_headings.html html=content beforeHeading="true" anchorBody="" anchorClass="anchor-heading" anchorAttrs="aria-labelledby=\"%html_id%\"" %} + {% else %} + {{ content }} {% endif %} -
-
-
-
-
- {{ content | toc }} -
- -
- {% if site.google_analytics %} - + {% if site.search_enabled != false %} + {% if site.search.button %} + + + + {% endif %} + +
{% endif %} - + + diff --git a/docs/assets/images/architecture_introduction_diagram.svg b/docs/assets/images/architecture_introduction_diagram.svg new file mode 100644 index 00000000000..3c9347e31a2 --- /dev/null +++ b/docs/assets/images/architecture_introduction_diagram.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/images/gotemplates/postman.png b/docs/assets/images/gotemplates/postman.png new file mode 100644 index 00000000000..4f6fb7fb9c6 Binary files /dev/null and b/docs/assets/images/gotemplates/postman.png differ diff --git a/docs/assets/images/gotemplates/swaggerui.png b/docs/assets/images/gotemplates/swaggerui.png new file mode 100644 index 00000000000..02bf9ce1cb7 Binary files /dev/null and b/docs/assets/images/gotemplates/swaggerui.png differ diff --git a/docs/docs/contributing/2020_season_of_docs.md b/docs/docs/contributing/2020_season_of_docs.md new file mode 100644 index 00000000000..649dcb427bb --- /dev/null +++ b/docs/docs/contributing/2020_season_of_docs.md @@ -0,0 +1,17 @@ +--- +layout: default +title: Google Season of Docs +nav_order: 1 +parent: Contributing +--- + +# 2020 Season of Docs + +
+ +
+ +The gRPC-Gateway participated in the 2020 [Google Season of Docs](https://g.co/seasonofdocs). +The project was completed by [@iamrajiv](https://github.com/iamrajiv). A summary of the project +work can be found in the +[project report](https://github.com/iamrajiv/GSoD-2020/blob/master/GSoD_2020_Project_Report.md). diff --git a/docs/docs/contributing/getting_started.md b/docs/docs/contributing/getting_started.md new file mode 100644 index 00000000000..f2b52520136 --- /dev/null +++ b/docs/docs/contributing/getting_started.md @@ -0,0 +1,10 @@ +--- +layout: default +title: Getting started +nav_order: 0 +parent: Contributing +--- + +# How to contribute + +See [CONTRIBUTING.md](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md). diff --git a/docs/docs/contributing/index.md b/docs/docs/contributing/index.md new file mode 100644 index 00000000000..4241bd25bc4 --- /dev/null +++ b/docs/docs/contributing/index.md @@ -0,0 +1,6 @@ +--- +layout: default +title: Contributing +nav_order: 5 +has_children: true +--- diff --git a/docs/docs/development/grpc-gateway_v2_migration_guide.md b/docs/docs/development/grpc-gateway_v2_migration_guide.md new file mode 100644 index 00000000000..fe3b131e983 --- /dev/null +++ b/docs/docs/development/grpc-gateway_v2_migration_guide.md @@ -0,0 +1,168 @@ +--- +layout: default +title: gRPC-Gateway v2 migration guide +nav_order: 0 +parent: Development +--- + +# gRPC-Gateway v2 migration guide + +This guide is supposed to help users of the gateway migrate from v1 to v2. See [the original issue](https://github.com/grpc-ecosystem/grpc-gateway/issues/1223) for detailed information on all changes that were made specifically to v2. + +The following behavioural defaults have been changed: + +## protoc-gen-swagger has been renamed protoc-gen-openapiv2 + +See [the original issue](https://github.com/grpc-ecosystem/grpc-gateway/issues/675) +for more information. Apart from the new name, the only real difference to users will be a slightly different proto annotation: + +```protobuf +import "protoc-gen-openapiv2/options/annotations.proto"; +``` + +instead of + +```protobuf +import "protoc-gen-swagger/options/annotations.proto"; +``` + +and + +```protobuf +option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { +``` + +instead of + +```protobuf +option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = { +``` + +The Bazel rule has been renamed `protoc_gen_openapiv2`. + +## The example field in the OpenAPI annotations is now a string + +This was a `google.protobuf.Any` type, but it was only used for the JSON representation, and it was breaking some tools and it was generally unclear to the user how it works. It is now a string instead. The value is copied verbatim to the output OpenAPI file. Remember to escape any quotes in the strings. + +For example, if you had an example that looked like this: + +```protobuf +example: { value: '{ "uuid": "0cf361e1-4b44-483d-a159-54dabdf7e814" }' } +``` + +It would now look like this: + +```protobuf +example: "{\"uuid\": \"0cf361e1-4b44-483d-a159-54dabdf7e814\"}" +``` + +See [a_bit_of_everything.proto](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/a_bit_of_everything.proto) in the example protos for more examples. + +## We now use the camelCase JSON names by default + +See [the original issue](https://github.com/grpc-ecosystem/grpc-gateway/issues/375) and +[original pull request](https://github.com/grpc-ecosystem/grpc-gateway/pull/540) for more information. + +If you want to revert to the old behaviour, configure a custom marshaler with `UseProtoNames: true`: + +```go +mux := runtime.NewServeMux( + runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{ + Marshaler: &runtime.JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + UseProtoNames: true, + EmitUnpopulated: true, + }, + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + }, + }), +) +``` + +To change the OpenAPI generator behaviour to match, set `json_names_for_fields=false` when generating: + +```sh +--openapiv2_out=json_names_for_fields=false:./gen/openapiv2 path/to/my/proto/v1/myproto.proto +``` + +If using the Bazel rule, set `json_names_for_fields=False`. + +## We now emit default values for all fields + +See [the original issue](https://github.com/grpc-ecosystem/grpc-gateway/issues/233) +for more information. + +If you want to revert to the old behaviour, configure a custom marshaler with +`EmitUnpopulated: false`: + +```go +mux := runtime.NewServeMux( + runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{ + Marshaler: &runtime.JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + EmitUnpopulated: false, + }, + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + }, + }), +) +``` + +## We now support google.api.HttpBody message types by default + +The `runtime.SetHTTPBodyMarshaler` function has disappeared, and is now +enabled by default. If you for some reason don't want `HttpBody` messages to be +respected, you can disable it by overwriting the default marshaler with one which +does not wrap `runtime.JSONPb` in `runtime.HTTPBodyMarshaler`: + +```go +mux := runtime.NewServeMux( + runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + EmitUnpopulated: true, + }, + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + }), +) +``` + +## runtime.DisallowUnknownFields has been removed + +All marshalling settings are now inherited from the configured marshaler. If you wish +to disallow unknown fields, configure a custom marshaler: + +```go +mux := runtime.NewServeMux( + runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{ + Marshaler: &runtime.JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + EmitUnpopulated: true, + }, + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: false, + }, + }, + }), +) +``` + +## WithLastMatchWins and allow_colon_final_segments=true is now default behaviour + +If you were previously specifying these, please remove them, as this is now +the default behaviour. See [the original issue](https://github.com/grpc-ecosystem/grpc-gateway/issues/224) for more information. + +There is no workaround for this, as we considered it a correct interpretation of the spec. If this breaks your application, carefully consider the order in which you define your services. + +## Error handling configuration has been overhauled + +`runtime.HTTPError`, `runtime.OtherErrorHandler`, `runtime.GlobalHTTPErrorHandler`, `runtime.WithProtoErrorHandler` are all gone. Error handling is rewritten around the use of gRPCs Status types. If you wish to configure how the gateway handles errors, please use `runtime.WithErrorHandler` and `runtime.WithStreamErrorHandler`. To handle routing errors (similar to the removed `runtime.OtherErrorHandler`) please use `runtime.WithRoutingErrorHandler`. + +## Default query parameter parsing behaviour change + +The default behaviour for query parameter parsing has changed to return an `InvalidArgument` (`400 Bad Request`) error when more than one of the same matching query parameters is parsed. Previously, it would log but not return an error, using the first query parameter that matched and ignoring any others. See [the original issue](https://github.com/grpc-ecosystem/grpc-gateway/issues/2632) for more information. \ No newline at end of file diff --git a/docs/docs/development/index.md b/docs/docs/development/index.md new file mode 100644 index 00000000000..f953f4bafc2 --- /dev/null +++ b/docs/docs/development/index.md @@ -0,0 +1,6 @@ +--- +layout: default +title: Development +nav_order: 4 +has_children: true +--- diff --git a/docs/_docs/cygwin.md b/docs/docs/development/installation_for_cygwin.md similarity index 55% rename from docs/_docs/cygwin.md rename to docs/docs/development/installation_for_cygwin.md index 5e0a37fafa6..2ed9ed8aefa 100644 --- a/docs/_docs/cygwin.md +++ b/docs/docs/development/installation_for_cygwin.md @@ -1,31 +1,36 @@ --- -category: documentation +layout: default title: Installation for Cygwin -order: 1000 +nav_order: 1 +parent: Development --- -#Installation for Cygwin +# Installation for Cygwin + +
+ +
-![cygwin-logo](https://upload.wikimedia.org/wikipedia/commons/thumb/2/29/Cygwin_logo.svg/145px-Cygwin_logo.svg.png) ## Installation -First you need to install the [Go language](https://golang.org/dl/). Please install the latest version, not the one that is listed here. + +First, you need to install the [Go language](https://golang.org/dl/). Please install the latest version, not the one that is listed here. wget -N https://storage.googleapis.com/golang/go1.8.1.windows-amd64.msi msiexec /i go1.8.1.windows-amd64.msi /passive /promptrestart -Then you need to install [ProtocolBuffers 3.0.0-beta-3](https://github.com/google/protobuf/releases) or later. Use the windows release while no native cygwin protoc with version 3 is available yet. +Then you need to install [ProtocolBuffers 3.0.0-beta-3](https://github.com/google/protobuf/releases) or later. Use the Windows release as no native Cygwin `protoc` with version 3 is available yet. wget -N https://github.com/google/protobuf/releases/download/v3.2.0/protoc-3.2.0-win32.zip` 7z x protoc-3.2.0-win32.zip -o/usr/local/ -Then you need to setup your Go workspace. Create the workspace dir. +Then you need to set up your Go workspace. Create the workspace dir. mkdir /home/user/go mkdir /home/user/go/bin mkdir /home/user/go/pkg mkdir /home/user/go/src -From an elevated cmd.exe prompt set the GOPATH variable in windows and add the `$GOPATH/bin` directory to your path using `reg add` instead of `setx` because [setx can truncated your PATH variable to 1024 characters](https://encrypted.google.com/search?hl=en&q=setx%20truncates%20PATH%201024#safe=off&hl=en&q=setx+truncated+PATH+1024). +From an elevated cmd.exe prompt set the GOPATH variable in Windows and add the `$GOPATH/bin` directory to your path using `reg add` instead of `setx` because [setx can truncate your PATH variable to 1024 characters](https://encrypted.google.com/search?hl=en&q=setx%20truncates%20PATH%201024#safe=off&hl=en&q=setx+truncated+PATH+1024). setx GOPATH c:\path\to\your\cygwin\home\user\go /M set pathkey="HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment" @@ -33,20 +38,20 @@ From an elevated cmd.exe prompt set the GOPATH variable in windows and add the ` Then `go get -u -v` the following packages: - go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway - go get -u -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger - go get -u -v github.com/golang/protobuf/protoc-gen-go + go get -u -v github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway + go get -u -v github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 + go get -u -v google.golang.org/protobuf/cmd/protoc-gen-go + go get -u -v google.golang.org/grpc/cmd/protoc-gen-go-grpc -This will probably fail with similar output. +This will probably fail with a similar output to this: github.com/grpc-ecosystem/grpc-gateway (download) # cd .; git clone https://github.com/grpc-ecosystem/grpc-gateway C:\path\to\your\cygwin\home\user\go\src\github.com\grpc-ecosystem\grpc-gateway Cloning into 'C:\path\to\your\cygwin\home\user\go\src\github.com\grpc-ecosystem\grpc-gateway'... fatal: Invalid path '/home/user/go/C:\path\to\your\cygwin\home\user\go\src\github.com\grpc-ecosystem\grpc-gateway': No such file or directory - package github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway: exit status 128 + package github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway: exit status 128 -To fix this you need to run the `go get -u -v` commands and look for all lines starting with `# cd .; `. -Copy and paste these lines into your shell and change the clone destination directories. +To fix this you need to run the `go get -u -v` commands and look for all lines starting with `# cd .;`. Copy and paste these lines into your shell and change the clone destination directories. git clone https://github.com/grpc-ecosystem/grpc-gateway $(cygpath -u $GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway git clone https://github.com/golang/glog $(cygpath -u $GOPATH)/src/github.com/golang/glog @@ -56,19 +61,20 @@ Copy and paste these lines into your shell and change the clone destination dire Once the clone operations are finished the `go get -u -v` commands shouldn't give you an error anymore. ## Usage -Follow the [instuctions](https://github.com/grpc-ecosystem/grpc-gateway#usage) in the [README](https://github.com/grpc-ecosystem/grpc-gateway). -Adjust steps 3, 5 and 7 like this. protoc expects native windows paths. +Follow the [instructions](https://github.com/grpc-ecosystem/grpc-gateway#usage) in the [README](https://github.com/grpc-ecosystem/grpc-gateway#readme). - protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --go_out=plugins=grpc:. ./path/to/your_service.proto - protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --grpc-gateway_out=logtostderr=true:. ./path/to/your_service.proto - protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis --swagger_out=logtostderr=true:. ./path/to/your_service.proto +Adjust steps 3, 5 and 7 like this. `protoc` expects native Windows paths. -Then `cd` into the directory where your entry-point `main.go` file is located and run + protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src --go_out=. --go-grpc_out=. ./path/to/your_service.proto + protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src --grpc-gateway_out=logtostderr=true:. ./path/to/your_service.proto + protoc -I. -I$(cygpath -w /usr/local/include) -I${GOPATH}/src --openapiv2_out=logtostderr=true:. ./path/to/your_service.proto + +Then `cd` into the directory where your entry-point `main.go` file is located and run: go get -v -This will fail like during the Installation. Look for all lines starting with `# cd .; ` and copy and paste these lines into your shell and change the clone destination directories. +This will fail in this same way as it did during the installation. Look for all lines starting with `# cd .;`. Copy and paste these lines into your shell and change the clone destination directories. git clone https://go.googlesource.com/net $(cygpath -u $GOPATH)/src/golang.org/x/net git clone https://go.googlesource.com/text $(cygpath -u $GOPATH)/src/golang.org/x/text @@ -76,8 +82,8 @@ This will fail like during the Installation. Look for all lines starting with `# Once the clone operations are finished the `go get -v` commands shouldn't give you an error anymore. -Then run +Then run: go install - -to compile and install your grpc-gateway service into `$GOPATH/bin`. + +This will compile and install your gRPC-Gateway service into `$GOPATH/bin`. diff --git a/docs/docs/faq.md b/docs/docs/faq.md new file mode 100644 index 00000000000..3d05c861a44 --- /dev/null +++ b/docs/docs/faq.md @@ -0,0 +1,64 @@ +--- +layout: default +title: FAQ +nav_order: 7 +--- + +# FAQ + +## How can I write the annotations which gRPC-Gateway requires? + +The gRPC-Gateway follows the spec of [`google.api.HttpRule`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto), so first check out the documentation if it is feasible in the spec. + +For situations where annotating the proto file is not an option please reference the documentation on [gRPC API Configuration](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/grpc_api_configuration/) + +See also [a past discussion](https://groups.google.com/d/msg/grpc-io/Xqx80hG0D44/VNCDHjeE6pUJ) in the grpc-io mailing list. + +## I want to support a certain style of HTTP request but the code generated by gRPC-Gateway does not. How can I support this style? + +See the question above at first. + +The gRPC-Gateway is intended to cover 80% of use cases without forcing you to write comprehensive but complicated annotations. So the gateway itself does not always cover all the use cases you have by design. In other words, the gateway automates typical boring boilerplate mapping between gRPC and HTTP/1 communication, but it does not do arbitrarily complex custom mappings for you. + +On the other hand, you can still add whatever you want as a middleware which wraps +[`runtime.ServeMux`](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#ServeMux). Since `runtime.ServeMux` is just a standard [`http.Handler`](http://golang.org/pkg/http#Handler), you can easily write a custom wrapper of `runtime.ServeMux`, leveraged with existing third-party libraries in Go (e.g. [gateway main.go program](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/gateway/main.go). + +## My gRPC server is written in (Scala or C++ or Ruby or Haskell etc). Is there a (Scala or C++ or Ruby or Haskell etc) version of gRPC-Gateway? + +As of now, No. But it should not be a big issue because the reverse-proxy which gRPC-Gateway generates usually works as an independent process and communicates with your gRPC server over TCP or a Unix domain sockets (Unix systems only). + +## Why are the models in the OpenAPI specification prefixed with the last part of the proto package name? + +The reason to generate the prefixes is that we don't have a guaranteed unique namespace. If two packages produce different `Foo` messages then we will have trouble. + +## Why not strip the prefix? + +When a message is added which happens to conflict with another message (e.g. by importing a message with the same name from a different package) it will break code that is very far away from the code that changed. This is in an effort to adhere to the [principle of least astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment). + +## What is the difference between the gRPC-Gateway and grpc-httpjson-transcoding? + +The gRPC-Gateway is a generator that generates a Go implementation of a JSON/HTTP-gRPC reverse proxy based on annotations in your proto file, while the [grpc-httpjson-transcoding](https://github.com/grpc-ecosystem/grpc-httpjson-transcoding) library doesn't require the generation step, it uses protobuf descriptors as config. It can be used as a component of an existing proxy. Google Cloud Endpoints and the gRPC-JSON transcoder filter in Envoy are using this. + + +**Behavior differences:** +- By default, gRPC-Gateway does not escape path parameters in the same way. [This can be configured.](../mapping/customizing_your_gateway.md#Controlling-path-parameter-unescaping) + +## What is the difference between the gRPC-Gateway and gRPC-web? + +### Usage + +In the gRPC-Gateway, we generate a reverse-proxy from the proto file annotations. In the front-end, we call directly through REST APIs. We can generate an OpenAPI v2 specification that may further be used to generate the frontend client from using `protoc-gen-openapiv2`. + +In gRPC-web, the client code is generated directly from the proto files and can be used in the frontend. + +### Performance + +The gRPC-Gateway parses JSON to the protobuf binary format before sending it to the gRPC server. It then has to parse the reply back from the protobuf binary format to JSON again The parsing overhead has a negative impact on performance. + +In gRPC-web, the message is sent in the protobuf binary format already, so there is no additional parsing cost on the proxy side. + +### Maintenance + +With the gRPC-Gateway, if your proto file changes, we have to regenerate the gateway reverse proxy code. If you are using the HTTP/JSON interface you probably have to change the front-end too, which means making changes in two places. + +In gRPC-web, regenerating the files from the proto file will automatically update the front-end client. diff --git a/docs/docs/mapping/binary_file_uploads.md b/docs/docs/mapping/binary_file_uploads.md new file mode 100644 index 00000000000..1a378cf8fe0 --- /dev/null +++ b/docs/docs/mapping/binary_file_uploads.md @@ -0,0 +1,54 @@ +--- +layout: default +title: Binary file uploads +nav_order: 2 +parent: Mapping +--- + +# Binary file uploads + +If you need to do a binary file upload, e.g. via; + +```sh +curl -X POST -F "attachment=@/tmp/somefile.txt" http://localhost:9090/v1/files +``` + +then your request will contain the binary data directly and there is no way to model this using gRPC. + +What you can do instead is to add a custom route directly on the `mux` instance. + +## Custom route on a mux instance + +Here we'll setup a handler (`handleBinaryFileUpload`) for `POST` requests: + +```go +// Create a mux instance +mux := runtime.NewServeMux() + +// Attachment upload from http/s handled manually +mux.HandlePath("POST", "/v1/files", handleBinaryFileUpload) +``` + +And then in your handler you can do something like: + +```go +func handleBinaryFileUpload(w http.ResponseWriter, r *http.Request, params map[string]string) { + err := r.ParseForm() + if err != nil { + http.Error(w, fmt.Sprintf("failed to parse form: %s", err.Error()), http.StatusBadRequest) + return + } + + f, header, err := r.FormFile("attachment") + if err != nil { + http.Error(w, fmt.Sprintf("failed to get file 'attachment': %s", err.Error()), http.StatusBadRequest) + return + } + defer f.Close() + + // + // Now do something with the io.Reader in `f`, i.e. read it into a buffer or stream it to a gRPC client side stream. + // Also `header` will contain the filename, size etc of the original file. + // +} +``` diff --git a/docs/docs/mapping/customizing_openapi_output.md b/docs/docs/mapping/customizing_openapi_output.md new file mode 100644 index 00000000000..a93828a508b --- /dev/null +++ b/docs/docs/mapping/customizing_openapi_output.md @@ -0,0 +1,826 @@ +--- +layout: default +title: Customizing OpenAPI Output +nav_order: 4 +parent: Mapping +--- + +{% raw %} + +# Customizing OpenAPI Output + +## In proto comments + +You can provide comments directly in your Protocol Buffer definitions and they will be translated into comments in the generated OpenAPI definitions: + +```protobuf +message MyMessage { + // This comment will end up direcly in your Open API definition + string uuid = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The UUID field."}]; +} +``` + +## Using proto options + +You can define options on your Protocol Buffer services, operations, messages, and field definitions to customize your Open API output. For instance, to customize the [OpenAPI Schema Object](https://swagger.io/specification/v2/#schemaObject) for messages and fields: + +```protobuf +import "protoc-gen-openapiv2/options/annotations.proto"; + +message ABitOfEverything { + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = { + json_schema: { + title: "A bit of everything" + description: "Intentionaly complicated message type to cover many features of Protobuf." + required: ["uuid", "int64_value", "double_value"] + } + external_docs: { + url: "https://github.com/grpc-ecosystem/grpc-gateway"; + description: "Find out more about ABitOfEverything"; + } + example: "{\"uuid\": \"0cf361e1-4b44-483d-a159-54dabdf7e814\"}" + extensions: { + key: "x-irreversible"; + value { + bool_value: true; + } + } + }; + + string uuid = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "The UUID field."}]; +} +``` + +Operations can also be customized: + +```protobuf +service ABitOfEverythingService { + rpc Delete(grpc.gateway.examples.internal.proto.sub2.IdMessage) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/example/a_bit_of_everything/{uuid}" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + security: { + security_requirement: { + key: "ApiKeyAuth"; + value: {} + } + security_requirement: { + key: "OAuth2"; + value: { + scope: "read"; + scope: "write"; + } + } + } + extensions: { + key: "x-irreversible"; + value { + bool_value: true; + } + } + }; + } +} +``` + +[Swagger Extensions](https://swagger.io/docs/specification/2-0/swagger-extensions/) can be added as key-value pairs to the options. Keys must begin with `x-` and values can be of any type listed [here](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#value). For example: +``` +extensions: { + key: "x-amazon-apigateway-authorizer"; + value { + struct_value { + fields { + key: "type"; + value { + string_value: "token"; + } + } + fields { + key: "authorizerResultTtlInSeconds"; + value { + number_value: 60; + } + } + } + } +} +``` + +Please see this [a_bit_of_everything.proto](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/a_bit_of_everything.proto) for examples of the options being used. + +## Using google.api.field_behavior + +Google provides an [field option](https://github.com/googleapis/googleapis/blob/master/google/api/field_behavior.proto) for defining the behavior of fields that is also supported: + +```protobuf +import "google/api/field_behavior.proto"; + +message MyMessage { + string a_required_field = 1 [(google.api.field_behavior) = REQUIRED]; +} +``` + +The following options are used in the Open API output: + +- `REQUIRED` - marks a field as required +- `OUTPUT_ONLY` - marks a field as readonly + +Google defines a couple of other options - `OPTIONAL`, `IMMUTABLE`, `INPUT_ONLY` - +that are not currently used. `OPTIONAL` support is currently under discussion +in [this issue](https://github.com/grpc-ecosystem/grpc-gateway/issues/669). + +For `IMMUTABLE` and `INPUT_ONLY` fields, there is an [open issue](https://github.com/OAI/OpenAPI-Specification/issues/1497) in the Open API specification for adding functionality for write-once or immutable fields to the spec. +## Using go templates in proto file comments + +Use [Go templates](https://golang.org/pkg/text/template/) in your proto file comments to allow more advanced documentation such as: + +- Documentation about fields in the proto objects. +- Import the content of external files (such as + [Markdown](https://en.wikipedia.org/wiki/Markdown)). + +### How to use it + +By default this function is turned off, so if you want to use it you have to add the `use_go_templates` option: + +```sh +--openapiv2_out . --openapiv2_opt use_go_templates=true +``` + +or: + +```sh +--openapiv2_out=use_go_templates=true:. +``` + +#### Example script + +Example of a bash script with the `use_go_templates` flag set to true: + +```sh +$ protoc -I. \ + --go_out . --go-grpc_out . \ + --grpc-gateway_out . --grpc-gateway_opt logtostderr=true \ + --openapiv2_out . \ + --openapiv2_opt logtostderr=true \ + --openapiv2_opt use_go_templates=true \ + path/to/my/proto/v1/myproto.proto +``` + +#### Example proto file + +Example of a proto file with Go templates. This proto file imports documentation from another file, `tables.md`: + +```protobuf +service LoginService { + // Login + // + // {{.MethodDescriptorProto.Name}} is a call with the method(s) {{$first := true}}{{range .Bindings}}{{if $first}}{{$first = false}}{{else}}, {{end}}{{.HTTPMethod}}{{end}} within the "{{.Service.Name}}" service. + // It takes in "{{.RequestType.Name}}" and returns a "{{.ResponseType.Name}}". + // + // {{import "tables.md"}} + rpc Login (LoginRequest) returns (LoginReply) { + option (google.api.http) = { + post: "/v1/example/login" + body: "*" + }; + } +} + +message LoginRequest { + // The entered username + string username = 1; + // The entered password + string password = 2; +} + +message LoginReply { + // Whether you have access or not + bool access = 1; +} +``` + +The content of `tables.md`: + +```markdown +## {{.RequestType.Name}} +| Field ID | Name | Type | Description | +| ----------- | --------- | --------------------------------------------------------- | ---------------------------- | {{range .RequestType.Fields}} +| {{.Number}} | {{.Name}} | {{if eq .Label.String "LABEL_REPEATED"}}[]{{end}}{{.Type}} | {{fieldcomments .Message .}} | {{end}} + +## {{.ResponseType.Name}} +| Field ID | Name | Type | Description | +| ----------- | --------- | ---------------------------------------------------------- | ---------------------------- | {{range .ResponseType.Fields}} +| {{.Number}} | {{.Name}} | {{if eq .Label.String "LABEL_REPEATED"}}[]{{end}}{{.Type}} | {{fieldcomments .Message .}} | {{end}} +``` + +### OpenAPI output + +#### SwaggerUI + +This is how the OpenAPI file would be rendered in [Swagger UI](https://swagger.io/tools/swagger-ui/). + +![Screenshot OpenAPI file in SwaggerUI](../../assets/images/gotemplates/swaggerui.png) + +#### Postman + +This is how the OpenAPI file would be rendered in [Postman](https://www.getpostman.com/). + +![Screenshot OpenAPI file in Postman](../../assets/images/gotemplates/postman.png) + +For a more detailed example of a proto file that has Go, templates enabled, [see the examples](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/use_go_template.proto). + +## Other plugin options + +A comprehensive list of OpenAPI plugin options can be found [here](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/protoc-gen-openapiv2/main.go). Options can be passed via `protoc` CLI: + +```sh +--openapiv2_out . --openapiv2_opt bar=baz,color=red +``` + +Or, with `buf` in `buf.gen.yaml`: + +```yaml + - name: openapiv2 + out: foo + opt: bar=baz,color=red +``` + +### Merging output + +If your protobuf definitions are spread across multiple files, the OpenAPI plugin will create a file for each `.proto` input. This may make sense for Go bindings, since they still share a package space, but fragmenting OpenAPI specifications across multiple files changes the schema itself. + +To merge disparate `.proto` inputs into a single OpenAPI file, use the `allow_merge` and `merge_file_name` options. + +`opt: allow_merge=true,merge_file_name=foo` will result in a single `foo.swagger.json`. Note that you may need to set +the [generation strategy](https://docs.buf.build/configuration/v1/buf-gen-yaml/#strategy) to `all` when merging many files: + +```yaml + - name: openapiv2 + out: foo + strategy: all + opt: allow_merge=true,merge_file_name=foo +``` + +### Enums as integers + +To generate enums as integers instead of strings, use `enums_as_ints`. + +`opt: enums_as_ints=true` will result in: + + +```json +{ + "name": "enumValue", + "description": " - Example enums", + "in": "query", + "required": false, + "type": "int", + "enum": [ + 0, + 1 + ], + "default": 0 +}, +``` + +### Omitting the default value of enums + +If you define enum types with non default value such as declaring 0 value with UNKNOWN and want to omit the default value from generated swagger file, use `omit_enum_default_value`. +This option also applies if enums_as_ints option is enalbled to generate enums as integer. + +`opt: omit_enum_default_value=true` will result in: + +Input Example: +``` +enum enumValue { + UNKNOWN = 0; + FOO = 1; +} +``` + +Output json: +```json +{ + "name": "enumValue", + "description": " - Example enums", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "FOO" + ] +}, +``` + +### Hiding fields, methods, services and enum values + +If you require internal or unreleased fields and APIs to be hidden from your API documentation, [`google.api.VisibilityRule`](https://github.com/googleapis/googleapis/blob/9916192ab15e3507e41ba2c5165182fec06120d0/google/api/visibility.proto#L89) annotations can be added to customize where they are generated. Combined with the option `visibility_restriction_selectors`, overlapping rules will appear in the OpenAPI output. + +`visibility_restriction_selectors` can be declared multiple times as an option to include multiple visibility restrictions in the output. +e.g. if you are using `buf`: + +```yaml +version: v1 +plugins: + - name: openapiv2 + out: . + opt: + - visibility_restriction_selectors=PREVIEW + - visibility_restriction_selectors=INTERNAL +``` + +or with `protoc` + +```sh +protoc --openapiv2_out=. --openapiv2_opt=visibility_restriction_selectors=PREVIEW --openapiv2_opt=visibility_restriction_selectors=INTERNAL ./path/to/file.proto +``` + +Elements without `google.api.VisibilityRule` annotations will appear as usual in the generated output. + +These restrictions and selectors are completely arbitrary and you can define whatever values or hierarchies you want. In this example we use `INTERNAL` and `PREVIEW`, but `INTERNAL`, `ALPHA`, `BETA`, `RELEASED`, or anything else could be used if you wish. + +Note: Annotations are only supported on Services, Methods, Fields and Enum Values. + +`opt: visibility_restriction_selectors=PREVIEW` will result in: + +Input Example: +```protobuf +service Echo { + rpc EchoInternal(VisibilityRuleSimpleMessage) returns (VisibilityRuleSimpleMessage) { + option (google.api.method_visibility).restriction = "INTERNAL"; + option (google.api.http) = { + get: "/v1/example/echo_internal" + }; + } + rpc EchoInternalAndPreview(VisibilityRuleSimpleMessage) returns (VisibilityRuleSimpleMessage) { + option (google.api.method_visibility).restriction = "INTERNAL,PREVIEW"; + option (google.api.http) = { + get: "/v1/example/echo_internal_and_preview" + }; + } +} + +message VisibilityRuleSimpleMessage { + enum VisibilityEnum { + UNSPECIFIED = 0; + VISIBLE = 1; + INTERNAL = 2 [(google.api.value_visibility).restriction = "INTERNAL"]; + PREVIEW = 3 [(google.api.value_visibility).restriction = "INTERNAL,PREVIEW"]; + } + + string internal_field = 1 [(google.api.field_visibility).restriction = "INTERNAL"]; + string preview_field = 2 [(google.api.field_visibility).restriction = "INTERNAL,PREVIEW"]; + VisibilityEnum an_enum = 3; +} +``` + +Output json: +```json +{ + "paths": { + "/v1/example/echo_internal_and_preview": { + "get": { + "summary": "EchoInternalAndPreview is a internal and preview API that should be visible in the OpenAPI spec.", + "operationId": "VisibilityRuleEchoService_EchoInternalAndPreview", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/examplepbVisibilityRuleSimpleMessage" + } + }, + "default": { + "description": "An unexpected error response.", + "schema": { + "$ref": "#/definitions/rpcStatus" + } + } + }, + "parameters": [ + { + "name": "previewField", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "anEnum", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "UNSPECIFIED", + "VISIBLE", + "PREVIEW" + ], + "default": "UNSPECIFIED" + } + ], + "tags": [ + "VisibilityRuleEchoService" + ] + } + } + } +} +``` + +For a more in depth example see [visibility_rule_echo_service.proto](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/visibility_rule_echo_service.proto) and the following output files for different values of `visibility_restriction_selectors`: +- [`visibility_restriction_selectors=PREVIEW`](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/visibility_rule_preview_echo_service.swagger.json) +- [`visibility_restriction_selectors=INTERNAL`](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/visibility_rule_internal_echo_service.swagger.json) +- [`visibility_restriction_selectors=INTERNAL,visibility_restriction_selectors=PREVIEW`](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/visibility_rule_preview_and_internal_echo_service.swagger.json) +- [Not set](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/visibility_rule_none_echo_service.swagger.json) + +### Path parameters + +When defining HTTP bindings with path parameters that contain multiple path segments, as suggested by the [Google AIPs](https://google.aip.dev/), the path parameter names are numbered to avoid generating duplicate paths in the OpenAPI file. + +For example, consider: +```protobuf +service LibraryService { + rpc GetShelf(GetShelfRequest) returns (Shelf) { + option (google.api.http) = { + get: "/v1/{name=shelves/*}" + }; + } + rpc GetBook(GetBookRequest) returns (Book) { + option (google.api.http) = { + get: "/v1/{name=shelves/*/books/*}" + }; + } +} + +message GetShelfRequest { + string name = 1; +} + +message GetBookRequest { + string name = 1; +} +``` + +This will generate the following paths: +- `/v1/{name}` +- `/v1/{name_1}` + +To override the path parameter names, annotate the field used as path parameter: +```protobuf +message GetShelfRequest { + string name = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {field_configuration: {path_param_name: "shelfName"}}]; +} +message GetBookRequest { + string name = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {field_configuration: {path_param_name: "bookName"}}]; +} +``` + +This will instead generate the following paths: +- `/v1/{shelfName}` +- `/v1/{bookName}` + +Note that path parameters in OpenAPI does not support values with `/`, as discussed in +[Support for path parameters which can contain slashes #892](https://github.com/OAI/OpenAPI-Specification/issues/892), +so tools as Swagger UI will URL encode any `/` provided as parameter value. A possible workaround for this is to write +a custom post processor for your OAS file to replace any path parameter with `/` into multiple parameters. + +### Output format + +By default the output format is JSON, but it is possible to configure it using the `output_format` option. Allowed values are: `json`, `yaml`. The output format will also change the extension of the output files. + +For example, if using `buf`: +```yaml + - name: openapiv2 + out: pkg + opt: output_format=yaml +``` + +Input example: +```protobuf +syntax = "proto3"; + +package helloproto.v1; +option go_package = "helloproto/v1;helloproto"; + +import "google/api/annotations.proto"; + +service EchoService { + rpc Hello(HelloReq) returns (HelloResp) { + option (google.api.http) = { + get: "/api/hello" + }; + } +} + +message HelloReq { + string name = 1; +} + +message HelloResp { + string message = 1; +} +``` + +Output: +```yaml +swagger: "2.0" +info: + title: helloproto/v1/example.proto + version: version not set +tags: +- name: EchoService +consumes: +- application/json +produces: +- application/json +paths: + /api/hello: + get: + operationId: EchoService_Hello + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/v1HelloResp' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/rpcStatus' + parameters: + - name: name + in: query + required: false + type: string + tags: + - EchoService +definitions: + protobufAny: + type: object + properties: + '@type': + type: string + additionalProperties: {} + rpcStatus: + type: object + properties: + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + $ref: '#/definitions/protobufAny' + v1HelloResp: + type: object + properties: + message: + type: string +``` + +### Disable service tag generation + +By default service tags are generated for backend services, but it is possible to disable it using the `disable_service_tags` option. Allowed values are: `true`, `false`. + +For example, if you are using `buf`: +```yaml +version: v1 +plugins: + - name: openapiv2 + out: . + opt: + - disable_service_tags=true +``` + +or with `protoc` + +```sh +protoc --openapiv2_out=. --openapiv2_opt=disable_service_tags=true ./path/to/file.proto +``` + +Input example: +```protobuf +syntax = "proto3"; + +package helloproto.v1; +option go_package = "helloproto/v1;helloproto"; + +import "google/api/annotations.proto"; + +service EchoService { + rpc Hello(HelloReq) returns (HelloResp) { + option (google.api.http) = { + get: "/api/hello" + }; + } +} + +message HelloReq { + string name = 1; +} + +message HelloResp { + string message = 1; +} +``` + +Output (tags object are not generated): +```yaml +swagger: "2.0" +info: + title: helloproto/v1/example.proto + version: version not set +consumes: + - application/json +produces: + - application/json +paths: + /api/hello: + get: + operationId: EchoService_Hello +``` + +### Disable default responses + +By default a 200 OK response is rendered for each service operation. But it is possible to disable this and explicitly define your service's responses, using the `disable_default_responses` option. Allowed values are: `true`, `false`. + +**Note**: This does not alter the behavior of the gateway itself and should be coupled with a `ForwardResponseWriter` when altering status codes, see [Controlling HTTP Response Codes](https://grpc-ecosystem.github.io/grpc-gateway/docs/mapping/customizing_your_gateway/#controlling-http-response-status-codes). + +For example, if you are using `buf`: + +```yaml +version: v1 +plugins: + - name: openapiv2 + out: . + opt: + - disable_default_responses=true +``` + +or with `protoc` + +```sh +protoc --openapiv2_out=. --openapiv2_opt=disable_default_responses=true ./path/to/file.proto +``` + +Input example: + +```protobuf +syntax = "proto3"; + +package helloproto.v1; + +import "google/api/annotations.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + +option go_package = "helloproto/v1;helloproto"; + +service EchoService { + rpc Hello(HelloReq) returns (HelloResp) { + option (google.api.http) = {get: "/api/hello"}; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + responses: { + key: "201", + value: { + description: "Created"; + schema: { + json_schema: {ref: ".helloproto.v1.HelloResp"} + } + } + }; + }; + } +} + +message HelloReq { + string name = 1; +} + +message HelloResp { + string message = 1; +} +``` + +Output (default response not generated): + +```yaml +swagger: "2.0" +info: + title: helloproto/v1/hello.proto + version: version not set +consumes: + - application/json +produces: + - application/json +paths: + /api/hello: + get: + operationId: EchoService_Hello + responses: + "201": + description: Created + schema: + $ref: "#/definitions/v1HelloResp" + parameters: + - name: name + in: query + required: false + type: string +definitions: + v1HelloResp: + type: object + properties: + message: + type: string +``` + +### Custom HTTP Header Request Parameters + +By default the parameters for each operation are generated from the protocol buffer definition however you can extend the parameters to include extra HTTP headers if required. + +**NOTE**: These annotations do not alter the behaviour of the gateway and must be coupled with custom header parsing behaviour in the application. Also be aware that adding header parameters can alter the forwards and backwards compatibility of the schema. You must also set a type for your header which can be one of `STRING`, `INTEGER`, `NUMBER` or `BOOLEAN`. + +```protobuf +syntax = "proto3"; + +package helloproto.v1; + +import "google/api/annotations.proto"; +import "protoc-gen-openapiv2/options/annotations.proto"; + +option go_package = "helloproto/v1;helloproto"; + +service EchoService { + rpc Hello(HelloReq) returns (HelloResp) { + option (google.api.http) = {get: "/api/hello"}; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + parameters: { + headers: { + name: "X-Foo"; + description: "Foo Header"; + type: STRING, + required: true; + }; + headers: { + name: "X-Bar"; + description: "Bar Header"; + type: NUMBER, + }; + }; + }; + } +} + +message HelloReq { + string name = 1; +} + +message HelloResp { + string message = 1; +} +``` + +Output: + +```yaml +swagger: "2.0" +info: + title: helloproto/v1/hello.proto + version: version not set +consumes: + - application/json +produces: + - application/json +paths: + /api/hello: + get: + operationId: Hello + responses: + "200": + description: A successful response. + schema: + $ref: "#/definitions/helloproto.v1.HelloResp" + parameters: + - name: name + in: query + required: false + type: string + - name: X-Foo + description: Foo Header + in: header + required: true + type: string + - name: X-Bar + description: Bar Header + in: header + required: false + type: number +definitions: + helloproto.v1.HelloResp: + type: object + properties: + message: + type: string +``` + +{% endraw %} diff --git a/docs/docs/mapping/customizing_your_gateway.md b/docs/docs/mapping/customizing_your_gateway.md new file mode 100644 index 00000000000..ca09c8c9d21 --- /dev/null +++ b/docs/docs/mapping/customizing_your_gateway.md @@ -0,0 +1,412 @@ +--- +layout: default +title: Customizing your gateway +nav_order: 5 +parent: Mapping +--- + +# Customizing your gateway + +## Message serialization + +### Custom serializer + +You might want to serialize request/response messages in MessagePack instead of JSON, for example: + +1. Write a custom implementation of [`Marshaler`](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#Marshaler). + +2. Register your marshaler with [`WithMarshalerOption`](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#WithMarshalerOption). + + e.g. + + ```go + var m your.MsgPackMarshaler + mux := runtime.NewServeMux( + runtime.WithMarshalerOption("application/x-msgpack", m), + ) + ``` + +You can see [the default implementation for JSON](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/runtime/marshal_jsonpb.go) for reference. + +### Using proto names in JSON + +The protocol buffer compiler generates camelCase JSON tags that are used by default. +If you want to use the exact case used in the proto files, set `UseProtoNames: true`: + +```go +mux := runtime.NewServeMux( + runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + UseProtoNames: true, + }, + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + }), +) +``` + +### Pretty-print JSON responses when queried with ?pretty + +You can have Elasticsearch-style `?pretty` support in your gateway's endpoints as follows: + +1. Wrap the ServeMux using a stdlib [`http.HandlerFunc`](https://golang.org/pkg/net/http/#HandlerFunc) that translates the provided query parameter into a custom `Accept` header. + +2. Register a pretty-printing marshaler for that MIME code. + +For example: + +```go +mux := runtime.NewServeMux( + runtime.WithMarshalerOption("application/json+pretty", &runtime.JSONPb{ + MarshalOptions: protojson.MarshalOptions{ + Indent: " ", + Multiline: true, // Optional, implied by presence of "Indent". + }, + UnmarshalOptions: protojson.UnmarshalOptions{ + DiscardUnknown: true, + }, + }), +) +prettier := func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // checking Values as map[string][]string also catches ?pretty and ?pretty= + // r.URL.Query().Get("pretty") would not. + if _, ok := r.URL.Query()["pretty"]; ok { + r.Header.Set("Accept", "application/json+pretty") + } + h.ServeHTTP(w, r) + }) +} +http.ListenAndServe(":8080", prettier(mux)) +``` + +Now, either when passing the header `Accept: application/json+pretty` or appending `?pretty` to your HTTP endpoints, the response will be pretty-printed. + +Note that this will conflict with any methods having input messages with fields named `pretty`; also, this example code does not remove the query parameter `pretty` from further processing. + +## Customize unmarshaling per Content-Type + +Having different unmarshaling options per Content-Type is as easy as configuring a custom marshaler: + +```go +mux := runtime.NewServeMux( + runtime.WithMarshalerOption("application/json+strict", &runtime.JSONPb{ + UnmarshalOptions: &protojson.UnmarshalOptions{ + DiscardUnknown: false, // explicit "false", &protojson.UnmarshalOptions{} would have the same effect + }, + }), +) +``` + +## Mapping from HTTP request headers to gRPC client metadata + +You might not like [the default mapping rule](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#DefaultHeaderMatcher) and might want to pass through all the HTTP headers, for example: + +1. Write a [`HeaderMatcherFunc`](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#HeaderMatcherFunc). + +2. Register the function with [`WithIncomingHeaderMatcher`](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#WithIncomingHeaderMatcher) + + e.g. + + ```go + func CustomMatcher(key string) (string, bool) { + switch key { + case "X-Custom-Header1": + return key, true + case "X-Custom-Header2": + return "custom-header2", true + default: + return key, false + } + } + + mux := runtime.NewServeMux( + runtime.WithIncomingHeaderMatcher(CustomMatcher), + ) + ``` + +To keep the [the default mapping rule](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#DefaultHeaderMatcher) alongside with your own rules write: + +```go +func CustomMatcher(key string) (string, bool) { + switch key { + case "X-User-Id": + return key, true + default: + return runtime.DefaultHeaderMatcher(key) + } +} +``` + +It will work with both: + +```sh +$ curl --header "x-user-id: 100d9f38-2777-4ee2-ac3b-b3a108f81a30" ... +``` + +and + +```sh +$ curl --header "X-USER-ID: 100d9f38-2777-4ee2-ac3b-b3a108f81a30" ... +``` + +To access this header on gRPC server side use: + +```go +userID := "" +if md, ok := metadata.FromIncomingContext(ctx); ok { + if uID, ok := md["x-user-id"]; ok { + userID = strings.Join(uID, ",") + } +} +``` + +## Mapping from gRPC server metadata to HTTP response headers + +Use [`WithOutgoingHeaderMatcher`](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#WithOutgoingHeaderMatcher). See [gRPC metadata docs](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) for more info on sending / receiving gRPC metadata, for example: + +```go +if appendCustomHeader { + grpc.SendHeader(ctx, metadata.New(map[string]string{ + "x-custom-header1": "value", + })) +} +``` + +## Mutate response messages or set response headers + +### Set HTTP headers + +You might want to return a subset of response fields as HTTP response headers; You might want to simply set an application-specific token in a header. Or you might want to mutate the response messages to be returned. + +1. Write a filter function. + +```go +func myFilter(ctx context.Context, w http.ResponseWriter, resp proto.Message) error { + t, ok := resp.(*externalpb.Tokenizer) + if ok { + w.Header().Set("X-My-Tracking-Token", t.Token) + t.Token = "" + } + return nil +} +``` + +2. Register the filter with [`WithForwardResponseOption`](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#WithForwardResponseOption) + +e.g. + +```go +mux := runtime.NewServeMux( + runtime.WithForwardResponseOption(myFilter), +) +``` + +### Controlling HTTP response status codes + +To have the most control over the HTTP response status codes, you can use custom metadata. + +While handling the rpc, set the intended status code: + +```go +_ = grpc.SetHeader(ctx, metadata.Pairs("x-http-code", "401")) +``` + +Now, before sending the HTTP response, we need to check for this metadata pair and explicitly set the status code for the response if found. +To do so, create a function and hook it into the gRPC-Gateway as a Forward Response Option. + +The function looks like this: + +```go +func httpResponseModifier(ctx context.Context, w http.ResponseWriter, p proto.Message) error { + md, ok := runtime.ServerMetadataFromContext(ctx) + if !ok { + return nil + } + + // set http status code + if vals := md.HeaderMD.Get("x-http-code"); len(vals) > 0 { + code, err := strconv.Atoi(vals[0]) + if err != nil { + return err + } + // delete the headers to not expose any grpc-metadata in http response + delete(md.HeaderMD, "x-http-code") + delete(w.Header(), "Grpc-Metadata-X-Http-Code") + w.WriteHeader(code) + } + + return nil +} +``` + +And it gets hooked into the gRPC-Gateway with: + +```go +gwMux := runtime.NewServeMux( + runtime.WithForwardResponseOption(httpResponseModifier), +) +``` + +Additional responses can be added to the Protocol Buffer definitions to match the new status codes: + +```protobuf +service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply) { + option (google.api.http) = { + post: "/v1/example/echo" + body: "*" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + responses: { + key: "201" + value: { + description: "A successful response." + schema: { + json_schema: { + ref: ".mypackage.HelloReply" + } + } + } + } + }; + } + + rpc SayGoodbye (GoodbyeRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v1/example/echo/{id}" + }; + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + responses: { + key: "204" + value: { + description: "A successful response." + schema: {} + } + } + }; + } +} +``` + +## Error handler + +To override error handling for a `*runtime.ServeMux`, use the +`runtime.WithErrorHandler` option. This will configure all unary error +responses to pass through this error handler. + +## Stream Error Handler + +The error handler described in the previous section applies only to RPC methods that have a unary response. + +When the method has a streaming response, gRPC-Gateway handles that by emitting a newline-separated stream of "chunks". Each chunk is an envelope that can contain either a response message or an error. Only the last chunk will include an error, and only when the RPC handler ends abnormally (i.e. with an error code). + +Because of the way the errors are included in the response body, the other error handler signature is insufficient. So for server streams, you must install a _different_ error handler: + +```go +mux := runtime.NewServeMux( + runtime.WithStreamErrorHandler(handleStreamError), +) +``` + +The signature of the handler is much more rigid because we need to know the structure of the error payload to properly encode the "chunk" schema into an OpenAPI spec. + +So the function must return a `*runtime.StreamError`. The handler can choose to omit some fields and can filter/transform the original error, such as stripping stack traces from error messages. + +Here's an example custom handler: + +```go +// handleStreamError overrides default behavior for computing an error +// message for a server stream. +// +// It uses a default "502 Bad Gateway" HTTP code, only emits "safe" +// messages and does not set the details field (so it will +// be omitted from the resulting JSON object that is sent to client). +func handleStreamError(ctx context.Context, err error) *status.Status { + code := codes.Internal + msg := "unexpected error" + if s, ok := status.FromError(err); ok { + code = s.Code() + // default message, based on the gRPC status + msg = s.Message() + // see if error details include "safe" message to send + // to external callers + for _, msg := range s.Details() { + if safe, ok := msg.(*SafeMessage); ok { + msg = safe.Text + break + } + } + } + return status.Errorf(code, msg) +} +``` + +If no custom handler is provided, the default stream error handler will include any gRPC error attributes (code, message, detail messages), if the error being reported includes them. If the error does not have these attributes, a gRPC code of `Unknown` (2) is reported. + +## Controlling path parameter unescaping + + + +By default, gRPC-Gateway unescapes the entire URL path string attempting to route a request. This causes routing errors when the path parameter contains an illegal character such as `/`. + +To replicate the behavior described in [google.api.http](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L224), use [runtime.WithUnescapingMode()](https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime?tab=doc#WithUnescapingMode) to configure the unescaping behavior, as in the example below: + +```go +mux := runtime.NewServeMux( + runtime.WithUnescapingMode(runtime.UnescapingModeAllExceptReserved), +) +``` + +For multi-segment parameters (e.g. `{id=**}`) [RFC 6570](https://tools.ietf.org/html/rfc6570) Reserved Expansion characters are left escaped and the gRPC API will need to unescape them. + +To replicate the default V2 escaping behavior but also allow passing pct-encoded `/` characters, the ServeMux can be configured as in the example below: + +```go +mux := runtime.NewServeMux( + runtime.WithUnescapingMode(runtime.UnescapingModeAllCharacters), +) +``` + +## Routing Error handler + +To override the error behavior when `*runtime.ServeMux` was not able to serve the request due to routing issues, use the `runtime.WithRoutingErrorHandler` option. + +This will configure all HTTP routing errors to pass through this error handler. The default behavior is to map HTTP error codes to gRPC errors. + +HTTP statuses and their mappings to gRPC statuses: + +- HTTP `404 Not Found` -> gRPC `5 NOT_FOUND` +- HTTP `405 Method Not Allowed` -> gRPC `12 UNIMPLEMENTED` +- HTTP `400 Bad Request` -> gRPC `3 INVALID_ARGUMENT` + +This method is not used outside of the initial routing. + +### Customizing Routing Errors + +If you want to retain HTTP `405 Method Not Allowed` instead of allowing it to be converted to the equivalent of the gRPC `12 UNIMPLEMENTED`, which is HTTP `501 Not Implmented` you can use the following example: + +```go +func handleRoutingError(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) { + if httpStatus != http.StatusMethodNotAllowed { + runtime.DefaultRoutingErrorHandler(ctx, mux, marshaler, writer, request, httpStatus) + return + } + + // Use HTTPStatusError to customize the DefaultHTTPErrorHandler status code + err := &HTTPStatusError{ + HTTPStatus: httpStatus + Err: status.Error(codes.Unimplemented, http.StatusText(httpStatus)) + } + + runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w , r, err) +} +``` + +To use this routing error handler, construct the mux as follows: +```go +mux := runtime.NewServeMux( + runtime.WithRoutingErrorHandler(handleRoutingError), +) +``` diff --git a/docs/docs/mapping/examples.md b/docs/docs/mapping/examples.md new file mode 100644 index 00000000000..48e8473164b --- /dev/null +++ b/docs/docs/mapping/examples.md @@ -0,0 +1,27 @@ +--- +layout: default +title: Examples +nav_order: 0 +parent: Mapping +--- + +# Examples + +Examples are available under `examples/internal` directory. + +- [`proto/examplepb/echo_service.proto`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/echo_service.proto), [`proto/examplepb/a_bit_of_everything.proto`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/a_bit_of_everything.proto), [`proto/examplepb/unannotated_echo_service.proto`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/unannotated_echo_service.proto): + protobuf service definitions. +- [`proto/examplepb/echo_service.pb.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/echo_service.pb.go), [`proto/examplepb/a_bit_of_everything.pb.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/a_bit_of_everything.pb.go), [`proto/examplepb/unannotated_echo_service.pb.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/unannotated_echo_service.pb.go): + generated Go service stubs and types. +- [`proto/examplepb/echo_service.pb.gw.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/echo_service.pb.gw.go), [`proto/examplepb/a_bit_of_everything.pb.gw.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/a_bit_of_everything.pb.gw.go), [`proto/examplepb/unannotated_echo_service.pb.gw.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/unannotated_echo_service.pb.gw.go): + generated gRPC-Gateway clients. +- [`proto/examplepb/unannotated_echo_service.yaml`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/unannotated_echo_service.yaml): + gRPC API Configuration for `unannotated_echo_service.proto`. +- [`server/main.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/server/main.go): + service implementation. +- [`main.go`](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/gateway/main.go): + entrypoint of the generated reverse proxy. + +To use the same port for custom HTTP handlers (e.g. serving `swagger.json`), +gRPC-Gateway, and a gRPC server, see [this code example by CoreOS](https://github.com/philips/grpc-gateway-example/blob/master/cmd/serve.go) (and it's accompanying +[blog post](https://coreos.com/blog/grpc-protobufs-swagger.html)). diff --git a/docs/docs/mapping/grpc_api_configuration.md b/docs/docs/mapping/grpc_api_configuration.md new file mode 100644 index 00000000000..6d69bbcc60f --- /dev/null +++ b/docs/docs/mapping/grpc_api_configuration.md @@ -0,0 +1,142 @@ +--- +layout: default +title: gRPC API Configuration +nav_order: 3 +parent: Mapping +--- + +# gRPC API Configuration + +In some situations annotating the proto file of service is not an option. For example, you might not have control over the proto file, or you might want to expose the same gRPC API multiple times in completely different ways. + +gRPC-Gateway supports 2 ways of dealing with these situations: + +- [gRPC API Configuration](#grpc-api-configuration) + - [`generate_unbound_methods`](#generate_unbound_methods) + - [Using an external configuration file](#using-an-external-configuration-file) + - [Usage of gRPC API Configuration YAML files](#usage-of-grpc-api-configuration-yaml-files) + +## `generate_unbound_methods` + +Providing this parameter to the `protoc` plugin will make it produce the HTTP mapping even for methods without any `HttpRule` annotation. This is similar to how [Cloud Endpoints behaves](https://cloud.google.com/endpoints/docs/grpc/transcoding#where_to_configure_transcoding) and uses the way [gRPC itself](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md) maps to HTTP/2: + +- HTTP method is `POST` +- URI path is built from the service's name and method: `//` (e.g.: `/my.package.EchoService/Echo`) +- HTTP body is the serialized protobuf message. + +NOTE: the same option is also supported by the `gen-openapiv2` plugin. + +## Using an external configuration file + +Google Cloud Platform offers a way to do this for services +hosted with them called ["gRPC API Configuration"](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config). It can be used to define the behavior of a gRPC API service without modifications to the service itself in the form of [YAML](https://en.wikipedia.org/wiki/YAML) configuration files. + +gRPC-Gateway generators implement the [HTTP rules part](https://cloud.google.com/endpoints/docs/grpc-service-config/reference/rpc/google.api#httprule) of this specification. This allows you to take a completely unannotated service proto file, add a YAML file describing its HTTP endpoints and use them together like an annotated proto file with the gRPC-Gateway generators. + +OpenAPI options may also be configured via ["OpenAPI Configuration"](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/internal/descriptor/openapiconfig/openapiconfig.proto) in the form of YAML configuration files. + +### Usage of gRPC API Configuration YAML files + +The following is equivalent to the basic [`README.md`](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/README.md#usage) example but without direct +annotation for gRPC-Gateway in the proto file. Only some steps require minor changes to use a gRPC API Configuration YAML file instead: + +1. Define your service in gRPC as usual + + your_service.proto: + + ```protobuf + syntax = "proto3"; + package your.service.v1; + option go_package = "github.com/yourorg/yourprotos/gen/go/your/service/v1"; + message StringMessage { + string value = 1; + } + + service YourService { + rpc Echo(StringMessage) returns (StringMessage) {} + } + ``` + +2. Instead of annotating the proto file in this step leave it untouched + and create a `your_service.yaml` with the following content: + + ```yaml + type: google.api.Service + config_version: 3 + + http: + rules: + - selector: your.service.v1.YourService.Echo + post: /v1/example/echo + body: "*" + ``` + + Use a [linter](http://www.yamllint.com/) to validate your YAML. + +3. Generate gRPC stub as before + + ```sh + protoc -I . \ + --go_out ./gen/go/ \ + --go_opt paths=source_relative \ + --go-grpc_out ./gen/go/ \ + --go-grpc_opt paths=source_relative \ + your/service/v1/your_service.proto + ``` + +It will generate a stub file with path `./gen/go/your/service/v1/your_service.pb.go`. + +4. Implement your service in gRPC as usual + +5. Generate the reverse-proxy. Here we have to pass the path to + the `your_service.yaml` in addition to the proto file: + + ```sh + protoc -I . \ + --grpc-gateway_out ./gen/go \ + --grpc-gateway_opt logtostderr=true \ + --grpc-gateway_opt paths=source_relative \ + --grpc-gateway_opt grpc_api_configuration=path/to/your_service.yaml \ + your/service/v1/your_service.proto + ``` + + This will generate a reverse proxy `gen/go/your/service/v1/your_service.pb.gw.go` that is identical to the one produced for the annotated proto. + + In situations where you only need the reverse-proxy you can use the `standalone=true` option when generating the code. This will ensure the `types` used within `your_service.pb.gw.go` reference the external source appropriately. + + ``` + protoc -I . \ + --grpc-gateway_out ./gen/go \ + --grpc-gateway_opt logtostderr=true \ + --grpc-gateway_opt paths=source_relative \ + --grpc-gateway_opt standalone=true \ + --grpc-gateway_opt grpc_api_configuration=path/to/your_service.yaml \ + your/service/v1/your_service.proto + ``` + +6. Generate the optional your_service.swagger.json + + ```sh + protoc -I . --openapiv2_out ./gen/go \ + --openapiv2_opt grpc_api_configuration=path/to/your_service.yaml \ + your/service/v1/your_service.proto + ``` + + or using an OpenAPI configuration file + + ```sh + protoc -I . --openapiv2_out ./gen/go \ + --openapiv2_opt grpc_api_configuration=path/to/your_service.yaml \ + --openapiv2_opt openapi_configuration=path/to/your_service_swagger.yaml \ + your/service/v1/your_service.proto + ``` + + For an example of an OpenAPI configuration file, see [unannotated_echo_service.swagger.yaml](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml), which adds OpenAPI options to [unannotated_echo_service.proto](https://github.com/grpc-ecosystem/grpc-gateway/tree/master/examples/internal/proto/examplepb/unannotated_echo_service.proto). + + ```sh + protoc -I . --openapiv2_out ./gen/go \ + --openapiv2_opt grpc_api_configuration=path/to/your_service.yaml \ + your/service/v1/your_service.proto + ``` + +All other steps work as before. If you want you can remove the `googleapis` include path in step 3 and 4 as the unannotated proto no longer requires them. diff --git a/docs/docs/mapping/httpbody_messages.md b/docs/docs/mapping/httpbody_messages.md new file mode 100644 index 00000000000..4a3623b69bc --- /dev/null +++ b/docs/docs/mapping/httpbody_messages.md @@ -0,0 +1,67 @@ +--- +layout: default +title: HttpBody Messages +nav_order: 1 +parent: Mapping +--- + +# HttpBody Messages + +The [HTTPBody](https://github.com/googleapis/googleapis/blob/master/google/api/httpbody.proto) messages allow a response message to be specified with custom data content and a custom content-type header. The values included in the HTTPBody response will be used verbatim in the returned message from the gateway. Make sure you format your response carefully! + +## Example Usage + +1. Define your service in gRPC with an httpbody response message + +```protobuf +import "google/api/httpbody.proto"; +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; + +service HttpBodyExampleService { + rpc HelloWorld(google.protobuf.Empty) returns (google.api.HttpBody) { + option (google.api.http) = { + get: "/helloworld" + }; + } + rpc Download(google.protobuf.Empty) returns (stream google.api.HttpBody) { + option (google.api.http) = { + get: "/download" + }; + } +} +``` + +3. Generate gRPC and reverse-proxy stubs and implement your service. + +## Example service implementation + +```go +func (*HttpBodyExampleService) Helloworld(ctx context.Context, in *empty.Empty) (*httpbody.HttpBody, error) { + return &httpbody.HttpBody{ + ContentType: "text/html", + Data: []byte("Hello World"), + }, nil +} + +func (HttpBodyExampleService) Download(_ *empty.Empty, stream HttpBodyExampleService_DownloadServer) error { + msgs := []*httpbody.HttpBody{ + { + ContentType: "text/html", + Data: []byte("Hello 1"), + }, + { + ContentType: "text/html", + Data: []byte("Hello 2"), + }, + } + + for _, msg := range msgs { + if err := stream.Send(msg); err != nil { + return err + } + } + + return nil +} +``` diff --git a/docs/docs/mapping/index.md b/docs/docs/mapping/index.md new file mode 100644 index 00000000000..88e1a943f5d --- /dev/null +++ b/docs/docs/mapping/index.md @@ -0,0 +1,6 @@ +--- +layout: default +title: Mapping +nav_order: 2 +has_children: true +--- diff --git a/docs/docs/mapping/patch_feature.md b/docs/docs/mapping/patch_feature.md new file mode 100644 index 00000000000..61cb8d24380 --- /dev/null +++ b/docs/docs/mapping/patch_feature.md @@ -0,0 +1,83 @@ +--- +layout: default +title: Patch feature +nav_order: 2 +parent: Mapping +--- + +# Patch feature + +The HTTP PATCH method allows a resource to be partially updated. + +If a binding is mapped to patch and the request message has exactly one FieldMask message in it, additional code is rendered for the gateway handler that will populate the FieldMask based on the request body. FieldMask is treated as a regular field by the gateway if the request method is not PATCH, or if the HttpRule body is `"*"` + +There are two scenarios: + +- The FieldMask is hidden from the REST request as per the + [Google API design guide](https://cloud.google.com/apis/design/standard_methods#update) (as in the first additional binding in the + [UpdateV2](https://github.com/grpc-ecosystem/grpc-gateway/blob/370d869f65d1ffb3d07187fb0db238eca2371ce3/examples/internal/proto/examplepb/a_bit_of_everything.proto#L428-L431) example). In this case, the FieldMask is updated from the request body and set in the gRPC request message. +- The FieldMask is exposed to the REST request (as in the second additional binding in the [UpdateV2](https://github.com/grpc-ecosystem/grpc-gateway/blob/370d869f65d1ffb3d07187fb0db238eca2371ce3/examples/internal/proto/examplepb/a_bit_of_everything.proto#L432-L435) example). For this case, the field mask is left untouched by the gateway. + +## Example Usage + +1. Create a PATCH request. + + The PATCH request needs to include the message and the update mask. + + ```protobuf + // UpdateV2Request request for update includes the message and the update mask + message UpdateV2Request { + ABitOfEverything abe = 1; + google.protobuf.FieldMask update_mask = 2; + } + ``` + +2. Define your service in gRPC + + If you want to use PATCH with fieldmask hidden from REST request only include the request message in the body. + + ```protobuf + rpc UpdateV2(UpdateV2Request) returns (google.protobuf.Empty) { + option (google.api.http) = { + put: "/v2/example/a_bit_of_everything/{abe.uuid}" + body: "abe" + additional_bindings { + patch: "/v2/example/a_bit_of_everything/{abe.uuid}" + body: "abe" + } + }; + } + ``` + + If you want to use PATCH with fieldmask exposed to the REST request then include the entire request message. + + ```protobuf + rpc UpdateV2(UpdateV2Request) returns (google.protobuf.Empty) { + option (google.api.http) = { + patch: "/v2a/example/a_bit_of_everything/{abe.uuid}" + body: "*" + }; + } + ``` + +3. Generate gRPC and reverse-proxy stubs and implement your service. + +## cURL examples + +In the example below, we will partially update our ABitOfEverything resource by passing only the field we want to change. Since we are using the endpoint with field mask hidden we only need to pass the field we want to change ("string_value") and it will keep everything else in our resource the same. + +```sh +$ curl \ + --data '{"stringValue": "strprefix/foo"}' \ + -X PATCH \ + http://address:port/v2/example/a_bit_of_everything/1 +``` + +If we know what fields we want to update then we can use PATCH with field mask approach. For this, we need to pass the resource and the update_mask. Below only the "single_nested" will get updated because that is what we specify in the field_mask. + +```sh +$ curl \ + --data '{"abe":{"singleNested":{"amount":457},"stringValue":"some value that will not get updated because not in the field mask"},"updateMask":"singleNested"}}' \ + -X PATCH \ + http://address:port/v2a/example/a_bit_of_everything/1 +``` diff --git a/docs/docs/operations/annotated_context.md b/docs/docs/operations/annotated_context.md new file mode 100644 index 00000000000..1eaaddac0b6 --- /dev/null +++ b/docs/docs/operations/annotated_context.md @@ -0,0 +1,52 @@ +--- +layout: default +title: Extracting the HTTP path pattern for a request +nav_order: 4 +parent: Operations +--- + +# Extracting the HTTP path pattern for a request + +It is often interesting to know what [HTTP path pattern](https://github.com/googleapis/googleapis/blob/869d32e2f0af2748ab530646053b23a2b80d9ca5/google/api/http.proto#L61-L87) was matched for a specific request, for example for metrics. This article explains how to extract the HTTP path pattern from the request context. + +## Get HTTP Path pattern +1. Define the HTTP path in the proto annotation. For example: + +```proto +syntax = "proto3"; +option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/proto/examplepb"; +package grpc.gateway.examples.internal.proto.examplepb; + +import "google/api/annotations.proto"; + +service LoginService { + rpc Login (LoginRequest) returns (LoginReply) { + option (google.api.http) = { + post: "/v1/example/login" + body: "*" + }; + } +} + +message LoginRequest {} + +message LoginReply {} +``` + +2. At runtime, get the HTTP path pattern from the annotated context, for example using the `WithMetadata` function. +You can pass data to your backend by adding them to the gRPC metadata or push them to a metrics server. + +```go +mux := runtime.NewServeMux( + runtime.WithMetadata(func(ctx context.Context, r *http.Request) metadata.MD { + md := make(map[string]string) + if method, ok := runtime.RPCMethod(ctx); ok { + md["method"] = method // /grpc.gateway.examples.internal.proto.examplepb.LoginService/Login + } + if pattern, ok := runtime.HTTPPathPattern(ctx); ok { + md["pattern"] = pattern // /v1/example/login + } + return metadata.New(md) + }), +) +``` \ No newline at end of file diff --git a/docs/docs/operations/aws_gateway_integration.md b/docs/docs/operations/aws_gateway_integration.md new file mode 100644 index 00000000000..0544512fdec --- /dev/null +++ b/docs/docs/operations/aws_gateway_integration.md @@ -0,0 +1,19 @@ +--- +layout: default +title: AWS gateway integration +nav_order: 3 +parent: Operations +--- + +# AWS gateway integration + +## Import OpenAPI documentation into AWS API Gateway + +The AWS API Gateway service allows importing of an OpenAPI specification to create a REST API. The process is very straightforward and can be found [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-import-api.html). + +Here are some tips to consider when importing the documentation: + +1. Remove any circular dependencies (these aren't supported by the parser). +2. Remove security-related annotations (These annotations aren't well supported by the parser). +3. Max length of fields are reviewed by the parser but the errors aren't self-explanatory. Review the [specification](https://swagger.io/specification/v2/) to verify that the requirements are met. +4. API gateway errors aren't great, but you can use this [page](https://apidevtools.org/swagger-parser/online/) for structure validation. diff --git a/docs/docs/operations/health_check.md b/docs/docs/operations/health_check.md new file mode 100644 index 00000000000..b8a8be5a3ee --- /dev/null +++ b/docs/docs/operations/health_check.md @@ -0,0 +1,60 @@ +--- +layout: default +title: Health check +nav_order: 1 +parent: Operations +--- + +# Health check + +## With the [gRPC Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) + +To use the gRPC health checking protocol you must add the two health checking methods, `Watch` and `Check`. + +## Registering the health server + +1. Add `google.golang.org/grpc/health/grpc_health_v1` to your imports +2. Register the health server with `grpc_health_v1.RegisterHealthServer(grpcServer, yourService)` + +## Adding the health check methods + +1. Check method + +```go +func (s *serviceServer) Check(ctx context.Context, in *health.HealthCheckRequest) (*health.HealthCheckResponse, error) { + return &health.HealthCheckResponse{Status: health.HealthCheckResponse_SERVING}, nil +} +``` + +2. Watch method + +```go +func (s *serviceServer) Watch(in *health.HealthCheckRequest, _ health.Health_WatchServer) error { + // Example of how to register both methods but only implement the Check method. + return status.Error(codes.Unimplemented, "unimplemented") +} +``` + +3. You can test the functionality with [GRPC health probe](https://github.com/grpc-ecosystem/grpc-health-probe). + +## Adding `/healthz` endpoint to runtime.ServeMux + +To automatically register a `/healthz` endpoint in your `ServeMux` you can use +the `ServeMuxOption` `WithHealthzEndpoint` +which takes in a connection to your registered gRPC server. + +This endpoint will forward a request to the `Check` method described above to really check the health of the +whole system, not only the gateway itself. If your server doesn't implement the health checking protocol each request +to `/healthz` will result in the following: + +```json +{"code":12,"message":"unknown service grpc.health.v1.Health","details":[]} +``` + +If you've implemented multiple services in your server you can target specific services with the `?service=` +query parameter. This will then be added to the `health.HealthCheckRequest` in the `Service` property. With that you can +write your own logic to handle that in the health checking methods. + +Analogously, to register an `{/endpoint/path}` endpoint in your `ServeMux` with a user-defined endpoint path, you can use +the `ServeMuxOption` `WithHealthEndpointAt`, which accepts a connection to your registered gRPC server +together with a custom `endpointPath string` parameter. diff --git a/docs/docs/operations/index.md b/docs/docs/operations/index.md new file mode 100644 index 00000000000..9ec5ab4b6fe --- /dev/null +++ b/docs/docs/operations/index.md @@ -0,0 +1,6 @@ +--- +layout: default +title: Operations +nav_order: 3 +has_children: true +--- diff --git a/docs/docs/operations/inject_router.md b/docs/docs/operations/inject_router.md new file mode 100644 index 00000000000..d3969b653d9 --- /dev/null +++ b/docs/docs/operations/inject_router.md @@ -0,0 +1,54 @@ +--- +layout: default +title: Adding custom routes to the mux +nav_order: 0 +parent: Operations +--- + +# Adding custom routes to the mux + +The gRPC-Gateway allows you to add custom routes to the serve mux, for example, if you want to support a use case that isn't supported by the gRPC-Gateway, like file uploads. + +## Example + +```go +package main + +import ( + "context" + "net/http" + + pb "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/helloworld" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" +) + +func main() { + ctx := context.TODO() + mux := runtime.NewServeMux() + // Register generated routes to mux + err := pb.RegisterGreeterHandlerServer(ctx, mux, &GreeterServer{}) + if err != nil { + panic(err) + } + // Register custom route for GET /hello/{name} + err = mux.HandlePath("GET", "/hello/{name}", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) { + w.Write([]byte("hello " + pathParams["name"])) + }) + if err != nil { + panic(err) + } + http.ListenAndServe(":8080", mux) +} + +// GreeterServer is the server API for Greeter service. +type GreeterServer struct { + +} + +// SayHello implement to say hello +func (h *GreeterServer) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) { + return &pb.HelloReply{ + Message: "hello " + req.Name, + }, nil +} +``` diff --git a/docs/docs/operations/tracing.md b/docs/docs/operations/tracing.md new file mode 100644 index 00000000000..219d6a859cf --- /dev/null +++ b/docs/docs/operations/tracing.md @@ -0,0 +1,182 @@ +--- +layout: default +title: Tracing +nav_order: 2 +parent: Operations +--- + +# Tracing + +## With [OpenCensus.io](https://opencensus.io/) and [AWS X-ray](https://aws.amazon.com/xray/) + +### Adding tracing using AWS-Xray as the exporter + +This example uses the AWS-Xray exporter with a global trace setting. Note that AWS X-ray exporter does not handle any metrics only tracing. + +1. Add the following imports + +```go +xray "contrib.go.opencensus.io/exporter/aws" +"go.opencensus.io/plugin/ocgrpc" +"go.opencensus.io/plugin/ochttp" +"go.opencensus.io/trace" +``` + +2. Register the AWS X-ray exporter for the GRPC server + +```go +xrayExporter, err := xray.NewExporter( + xray.WithVersion("latest"), + // Add your AWS region. + xray.WithRegion("ap-southeast-1"), +) +if err != nil { + // Handle any error. +} +// Do not forget to call Flush() before the application terminates. +defer xrayExporter.Flush() + +// Register the trace exporter. +trace.RegisterExporter(xrayExporter) +``` + +3. Add a global tracing configuration + +```go +// Always trace in this example. +// In production this can be set to a trace.ProbabilitySampler. +trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) +``` + +4. Add `ocgrpc.ClientHandler` for tracing the gRPC client calls + +```go +// Example using DialContext +conn, err := grpc.DialContext( + // Other options goes here. + // Add ocgrpc.ClientHandler for tracing the grpc client calls. + grpc.WithStatsHandler(&ocgrpc.ClientHandler{}), +) +``` + +5. Wrap the gateway mux with the OpenCensus HTTP handler + +```go +gwmux := runtime.NewServeMux() + +openCensusHandler := &ochttp.Handler{ + Handler: gwmux, +} + +gwServer := &http.Server{ + Addr: "0.0.0.0:10000", + Handler: openCensusHandler, + }), +} +``` + +### Without a global configuration + +In this example we have added the [gRPC Health Checking Protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) and we do not wish to trace any health checks. + +1. Follow step `1`, `2` and `4` from the previous section. + +2. Since we are not using a global configuration we can decide what paths we want to trace. + +```go +gwmux := runtime.NewServeMux() + +openCensusHandler := &ochttp.Handler{ + Handler: gwmux, + GetStartOptions: func(r *http.Request) trace.StartOptions { + startOptions := trace.StartOptions{} + if strings.HasPrefix(r.URL.Path, "/api") { + // This example will always trace anything starting with /api. + startOptions.Sampler = trace.AlwaysSample() + } + return startOptions + }, +} +``` + +4. No global configuration means we have to use the [per span sampler](https://opencensus.io/tracing/sampling/#per-span-sampler). + +#### A method we want to trace + +```go +func (s *service) Name(ctx context.Context, req *pb.Request) (*pb.Response, error) { + // Here we add the span ourselves. + ctx, span := trace.StartSpan(ctx, "name.to.use.in.trace", trace. + // Select a sampler that fits your implementation. + WithSampler(trace.AlwaysSample())) + defer span.End() + /// Other stuff goes here. +} +``` + +#### A method we do not wish to trace + +```go +func (s *service) Check(ctx context.Context, in *health.HealthCheckRequest) (*health.HealthCheckResponse, error) { + // Note no span here. + return &health.HealthCheckResponse{Status: health.HealthCheckResponse_SERVING}, nil +} +``` + +## OpenTracing Support + +If your project uses [OpenTracing](https://github.com/opentracing/opentracing-go) and you'd like spans to propagate through the gateway, you can add some middleware which parses the incoming HTTP headers to create a new span correctly. + +```go +import ( + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/ext" +) + +var grpcGatewayTag = opentracing.Tag{Key: string(ext.Component), Value: "grpc-gateway"} + +func tracingWrapper(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + parentSpanContext, err := opentracing.GlobalTracer().Extract( + opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header)) + if err == nil || err == opentracing.ErrSpanContextNotFound { + serverSpan := opentracing.GlobalTracer().StartSpan( + "ServeHTTP", + // this is magical, it attaches the new span to the parent parentSpanContext, and creates an unparented one if empty. + ext.RPCServerOption(parentSpanContext), + grpcGatewayTag, + ) + r = r.WithContext(opentracing.ContextWithSpan(r.Context(), serverSpan)) + defer serverSpan.Finish() + } + h.ServeHTTP(w, r) + }) +} + +// Then just wrap the mux returned by runtime.NewServeMux() like this +if err := http.ListenAndServe(":8080", tracingWrapper(mux)); err != nil { + log.Fatalf("failed to start gateway server on 8080: %v", err) +} +``` + +Finally, don't forget to add a tracing interceptor when registering +the services. E.g. + +```go +import ( + "google.golang.org/grpc" + "github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing" +) + +opts := []grpc.DialOption{ + grpc.WithUnaryInterceptor( + grpc_opentracing.UnaryClientInterceptor( + grpc_opentracing.WithTracer(opentracing.GlobalTracer()), + ), + ), +} +if err := pb.RegisterMyServiceHandlerFromEndpoint(ctx, mux, serviceEndpoint, opts); err != nil { + log.Fatalf("could not register HTTP service: %v", err) +} +``` diff --git a/docs/docs/overview/background.md b/docs/docs/overview/background.md new file mode 100644 index 00000000000..9e0fbc84c65 --- /dev/null +++ b/docs/docs/overview/background.md @@ -0,0 +1,12 @@ +--- +layout: default +title: Background +nav_order: 0 +parent: Overview +--- + +# Background + +gRPC is great -- it generates API clients and server stubs in many programming languages, it is fast, easy-to-use, bandwidth-efficient and its design is combat-proven by Google. However, you might still want to provide a traditional RESTful API as well. Reasons can range from maintaining backwards-compatibility, supporting languages or clients not well supported by gRPC to simply maintaining the aesthetics and tooling involved with a RESTful architecture. + +This project aims to provide that HTTP+JSON interface to your gRPC service. A small amount of configuration in your service to attach HTTP semantics is all that's needed to generate a reverse-proxy with this library. diff --git a/docs/docs/overview/index.md b/docs/docs/overview/index.md new file mode 100644 index 00000000000..481fbe842c2 --- /dev/null +++ b/docs/docs/overview/index.md @@ -0,0 +1,6 @@ +--- +layout: default +title: Overview +nav_order: 1 +has_children: true +--- diff --git a/docs/docs/overview/usage.md b/docs/docs/overview/usage.md new file mode 100644 index 00000000000..d9e7b0545d5 --- /dev/null +++ b/docs/docs/overview/usage.md @@ -0,0 +1,10 @@ +--- +layout: default +title: How do I use this? +nav_order: 1 +parent: Overview +--- + +# How do I use this? + +Follow the [instructions](https://github.com/grpc-ecosystem/grpc-gateway#usage) in the [README](https://github.com/grpc-ecosystem/grpc-gateway#readme). diff --git a/docs/docs/related_projects.md b/docs/docs/related_projects.md new file mode 100644 index 00000000000..e9600bb9bd9 --- /dev/null +++ b/docs/docs/related_projects.md @@ -0,0 +1,23 @@ +--- +layout: default +title: Related projects +nav_order: 8 +--- + +# Related projects + +- [grpc-dynamic-gateway](https://github.com/konsumer/grpc-dynamic-gateway) + + A dynamically configured alternative to the grpc-gateway written in Node. + +- [rest2grpc](https://www.npmjs.com/package/rest2grpc) + + A statically configured alternative to the grpc-gateway written in Node. + +- The Envoy proxy [gRPC-JSON transcoder](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_json_transcoder_filter) + + An Envoy proxy filter that translates incoming JSON requests to gRPC and back. + +- Google Cloud Platform [HTTP/JSON gRPC transcoding](https://cloud.google.com/endpoints/docs/grpc/transcoding) + + A GCP product that behaves like the grpc-gateway. diff --git a/docs/docs/tutorials/adding_annotations.md b/docs/docs/tutorials/adding_annotations.md new file mode 100644 index 00000000000..bf0b13d7a23 --- /dev/null +++ b/docs/docs/tutorials/adding_annotations.md @@ -0,0 +1,215 @@ +--- +layout: default +title: Adding gRPC-Gateway annotations to an existing proto file +nav_order: 4 +parent: Tutorials +--- + +# Adding gRPC-Gateway annotations to an existing proto file + +Now that we've got a working Go gRPC server, we need to add the gRPC-Gateway annotations. + +The annotations define how gRPC services map to the JSON request and response. When using protocol buffers, each RPC must define the HTTP method and path using the `google.api.http` annotation. + +So we will need to add the `google/api/http.proto` import to the proto file. We also need to add the HTTP->gRPC mapping we want. In this case, we're mapping `POST /v1/example/echo` to our `SayHello` RPC. + +```protobuf +syntax = "proto3"; + +package helloworld; + +import "google/api/annotations.proto"; + +// Here is the overall greeting service definition where we define all our endpoints +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) { + option (google.api.http) = { + post: "/v1/example/echo" + body: "*" + }; + } +} + +// The request message containing the user's name +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} +``` + +See [a_bit_of_everything.proto](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/a_bit_of_everything.proto) for examples of more annotations you can add to customize gateway behavior. + +## Generating the gRPC-Gateway stubs + +Now that we've got the gRPC-Gateway annotations added to the proto file, we need to use the gRPC-Gateway generator to generate the stubs. + +### Using buf + +We'll need to add the gRPC-Gateway generator to the generation configuration: + +```yaml +version: v1 +plugins: + - name: go + out: proto + opt: paths=source_relative + - name: go-grpc + out: proto + opt: paths=source_relative,require_unimplemented_servers=false + - name: grpc-gateway + out: proto + opt: paths=source_relative +``` + +We'll also need to add the `googleapis` dependency to our `buf.yaml` file: + +```yaml +version: v1 +name: buf.build/myuser/myrepo +deps: + - buf.build/googleapis/googleapis +``` + +Then we need to run `buf mod update` to select a version of the dependency to use. + +And that's it! Now if you run: + +```sh +$ buf generate +``` + +It should produce a `*.gw.pb.go` file. + +### Using `protoc` + +Before we can generate the stubs with `protoc`, we need to copy some dependencies into our proto file structure. Copy a subset of the `googleapis` +from the [official repository](https://github.com/googleapis/googleapis) to your local proto file structure. It should look like this afterwards: + +``` +proto +β”œβ”€β”€ google +β”‚ └── api +β”‚ β”œβ”€β”€ annotations.proto +β”‚ └── http.proto +└── helloworld + └── hello_world.proto +``` + +Now we need to add the gRPC-Gateway generator to the `protoc` invocation: + +```sh +$ protoc -I ./proto \ + --go_out ./proto --go_opt paths=source_relative \ + --go-grpc_out ./proto --go-grpc_opt paths=source_relative \ + --grpc-gateway_out ./proto --grpc-gateway_opt paths=source_relative \ + ./proto/helloworld/hello_world.proto +``` + +This should generate a `*.gw.pb.go` file. + +We also need to add and serve the gRPC-Gateway mux in our `main.go` file. + +```go +package main + +import ( + "context" + "log" + "net" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + helloworldpb "github.com/myuser/myrepo/proto/helloworld" +) + +type server struct{ + helloworldpb.UnimplementedGreeterServer +} + +func NewServer() *server { + return &server{} +} + +func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) { + return &helloworldpb.HelloReply{Message: in.Name + " world"}, nil +} + +func main() { + // Create a listener on TCP port + lis, err := net.Listen("tcp", ":8080") + if err != nil { + log.Fatalln("Failed to listen:", err) + } + + // Create a gRPC server object + s := grpc.NewServer() + // Attach the Greeter service to the server + helloworldpb.RegisterGreeterServer(s, &server{}) + // Serve gRPC server + log.Println("Serving gRPC on 0.0.0.0:8080") + go func() { + log.Fatalln(s.Serve(lis)) + }() + + // Create a client connection to the gRPC server we just started + // This is where the gRPC-Gateway proxies the requests + conn, err := grpc.DialContext( + context.Background(), + "0.0.0.0:8080", + grpc.WithBlock(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + log.Fatalln("Failed to dial server:", err) + } + + gwmux := runtime.NewServeMux() + // Register Greeter + err = helloworldpb.RegisterGreeterHandler(context.Background(), gwmux, conn) + if err != nil { + log.Fatalln("Failed to register gateway:", err) + } + + gwServer := &http.Server{ + Addr: ":8090", + Handler: gwmux, + } + + log.Println("Serving gRPC-Gateway on http://0.0.0.0:8090") + log.Fatalln(gwServer.ListenAndServe()) +} +``` + +For more examples, please refer to [our boilerplate repository](https://github.com/johanbrandhorst/grpc-gateway-boilerplate). + +## Testing the gRPC-Gateway + +Now we can start the server: + +```sh +$ go run main.go +``` + +Then we use cURL to send HTTP requests: + +```sh +$ curl -X POST -k http://localhost:8090/v1/example/echo -d '{"name": " hello"}' +``` + +``` +{"message":"hello world"} +``` + +Hopefully, that gives a bit of understanding of how to use the gRPC-Gateway. + +Full source code of hello world program can be found here [helloworld-grpc-gateway](https://github.com/iamrajiv/helloworld-grpc-gateway). + +[Next](learn_more.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } diff --git a/docs/docs/tutorials/creating_main.go.md b/docs/docs/tutorials/creating_main.go.md new file mode 100644 index 00000000000..de1f0ae7aca --- /dev/null +++ b/docs/docs/tutorials/creating_main.go.md @@ -0,0 +1,58 @@ +--- +layout: default +title: Creating main.go +nav_order: 3 +parent: Tutorials +--- + +# Creating main.go + +Before creating `main.go` file we are assuming that the user has created a `go.mod` with the name `github.com/myuser/myrepo`, if not please refer to [Creating go.mod file](introduction.md#creating-gomod-file). The import here is using the path to the generated files in `proto/helloworld` relative to the root of the repository. + +```go +package main + +import ( + "context" + "log" + "net" + + "google.golang.org/grpc" + + helloworldpb "github.com/myuser/myrepo/proto/helloworld" +) + +type server struct{ + helloworldpb.UnimplementedGreeterServer +} + +func NewServer() *server { + return &server{} +} + +func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) { + return &helloworldpb.HelloReply{Message: in.Name + " world"}, nil +} + +func main() { + // Create a listener on TCP port + lis, err := net.Listen("tcp", ":8080") + if err != nil { + log.Fatalln("Failed to listen:", err) + } + + // Create a gRPC server object + s := grpc.NewServer() + // Attach the Greeter service to the server + helloworldpb.RegisterGreeterServer(s, &server{}) + // Serve gRPC Server + log.Println("Serving gRPC on 0.0.0.0:8080") + log.Fatal(s.Serve(lis)) +} +``` + +## Read More + +For more refer to gRPC docs [https://grpc.io/docs/languages/go/](https://grpc.io/docs/languages/go/). + +[Next](adding_annotations.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } diff --git a/docs/docs/tutorials/generating_stubs/index.md b/docs/docs/tutorials/generating_stubs/index.md new file mode 100644 index 00000000000..ec873a6a5d3 --- /dev/null +++ b/docs/docs/tutorials/generating_stubs/index.md @@ -0,0 +1,9 @@ +--- +layout: default +title: Generating stubs +parent: Tutorials +nav_order: 2 +has_children: true +--- + +For generating the stubs, we have two alternatives: `protoc` and `buf`. `protoc` is the more classic generation experience that is used widely in the industry, but it has a pretty steep learning curve. `buf` is a newer tool that is built with user experience and speed in mind. It also offers linting and breaking change detection, something `protoc` doesn't offer. We offer instructions for both here. diff --git a/docs/docs/tutorials/generating_stubs/using_buf.md b/docs/docs/tutorials/generating_stubs/using_buf.md new file mode 100644 index 00000000000..f7d657fb4b9 --- /dev/null +++ b/docs/docs/tutorials/generating_stubs/using_buf.md @@ -0,0 +1,45 @@ +--- +layout: default +title: Generating stubs using buf +nav_order: 0 +parent: Generating stubs +grand_parent: Tutorials +--- + +# Generating stubs using buf + +[Buf](https://github.com/bufbuild/buf) is a tool that provides various protobuf utilities such as linting, breaking change detection and generation. Please find installation instructions on [https://docs.buf.build/installation/](https://docs.buf.build/installation/). + +It is configured through a `buf.yaml` file that should be checked in to the root of your Protobuf file hierarchy. Buf will automatically read this file if present. Configuration can also be provided via the command-line flag `--config`, which accepts a path to a `.json` or `.yaml` file, or direct JSON or YAML data. As opposed to `protoc`, where all `.proto` files are manually specified on the command-line, buf operates by recursively discovering all `.proto` files under configuration and building them. + +The following is an example of a valid configuration, and you would put it in the root of your Protobuf file hierarchy, e.g. in `proto/buf.yaml` relative to the root of your repository. + +```yaml +version: v1 +name: buf.build/myuser/myrepo +``` + +To generate type and gRPC stubs for Go, create the file `buf.gen.yaml`: + +```yaml +version: v1 +plugins: + - name: go + out: proto + opt: paths=source_relative + - name: go-grpc + out: proto + opt: paths=source_relative +``` + +We use the `go` and `go-grpc` plugins to generate Go types and gRPC service definitions. We're outputting the generated files relative to the `proto` folder, and we're using the `paths=source_relative` option, which means that the generated files will appear in the same directory as the source `.proto` file. + +Then run + +```sh +$ buf generate +``` + +This will have generated a `*.pb.go` and a `*_grpc.pb.go` file for each protobuf package in our `proto` file hierarchy. + +[Next](../creating_main.go.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } diff --git a/docs/docs/tutorials/generating_stubs/using_protoc.md b/docs/docs/tutorials/generating_stubs/using_protoc.md new file mode 100644 index 00000000000..b72eeb1120d --- /dev/null +++ b/docs/docs/tutorials/generating_stubs/using_protoc.md @@ -0,0 +1,24 @@ +--- +layout: default +title: Generating stubs using protoc +nav_order: 1 +parent: Generating stubs +grand_parent: Tutorials +--- + +# Generating stubs using protoc + +Here's an example of what a `protoc` command might look like to generate Go stubs, assuming that you're at the root of your repository and you have your proto files in a directory called `proto`: + +```sh +$ protoc -I ./proto \ + --go_out ./proto --go_opt paths=source_relative \ + --go-grpc_out ./proto --go-grpc_opt paths=source_relative \ + ./proto/helloworld/hello_world.proto +``` + +We use the `go` and `go-grpc` plugins to generate Go types and gRPC service definitions. We're outputting the generated files relative to the `proto` folder, and we're using the `paths=source_relative` option, which means that the generated files will appear in the same directory as the source `.proto` file. + +This will have generated a `*.pb.go` and a `*_grpc.pb.go` file for `proto/helloworld/hello_world.proto`. + +[Next](../creating_main.go.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md new file mode 100644 index 00000000000..ad3f51c5163 --- /dev/null +++ b/docs/docs/tutorials/index.md @@ -0,0 +1,6 @@ +--- +layout: default +title: Tutorials +nav_order: 6 +has_children: true +--- diff --git a/docs/docs/tutorials/introduction.md b/docs/docs/tutorials/introduction.md new file mode 100644 index 00000000000..d179c94a22d --- /dev/null +++ b/docs/docs/tutorials/introduction.md @@ -0,0 +1,55 @@ +--- +layout: default +title: Introduction to the gRPC-Gateway +nav_order: 0 +parent: Tutorials +--- + +# Introduction to the gRPC-Gateway + +We all know that gRPC is not a tool for everything. There are cases where we still want to provide a traditional HTTP/JSON API. The reasons can range from maintaining backward-compatibility to supporting programming languages or clients not well supported by gRPC. But writing another service just to expose an HTTP/JSON API is quite a time consuming and tedious task. + +So is there any way to code just once, but provide APIs in both gRPC and HTTP/JSON at the same time? + +The answer is Yes. + +The gRPC-Gateway is a plugin of the Google protocol buffers compiler [protoc](https://github.com/protocolbuffers/protobuf). It reads protobuf service definitions and generates a reverse-proxy server which translates a RESTful HTTP API into gRPC. This server is generated according to the [`google.api.http`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L46) annotations in your service definitions. + +This helps you provide your APIs in both gRPC and HTTP/JSON format at the same time. + +
+ +
+ +## Prerequisites + +Before we start coding, we have to install some tools. + +We will be using a Go gRPC server in the examples, so please install Go first from [https://golang.org/dl/](https://golang.org/dl/). + +After installing Go, use `go get` to download the following packages: + +```sh +$ go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway +$ go get google.golang.org/protobuf/cmd/protoc-gen-go +$ go get google.golang.org/grpc/cmd/protoc-gen-go-grpc +``` + +This installs the `protoc` generator plugins we need to generate the stubs. Make sure to add `$GOPATH/bin` to your `$PATH` so that executables installed via `go get` are available on your `$PATH`. + +We will be working in a new module for this tutorial, so go ahead and create that in a folder of your choosing now: + +### Creating go.mod file + +Start your module using the [go mod init command](https://golang.org/cmd/go/#hdr-Initialize_new_module_in_current_directory) to create a go.mod file. + +Run the `go mod init` command, giving it the path of the module your code will be in. Here, use github.com/myuser/myrepo for the module path -- in production code, this would be the URL from which your module can be downloaded. + +```sh +$ go mod init github.com/myuser/myrepo +go: creating new go.mod: module github.com/myuser/myrepo +``` + +The `go mod init` command creates a go.mod file that identifies your code as a module that might be used from other code. The file you just created includes only the name of your module and the Go version your code supports. But as you add dependencies -- meaning packages from other modules -- the go.mod file will list the specific module versions to use. This keeps builds reproducible and gives you direct control over which module versions to use. + +[Next](simple_hello_world.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } diff --git a/docs/docs/tutorials/learn_more.md b/docs/docs/tutorials/learn_more.md new file mode 100644 index 00000000000..b9d950da28e --- /dev/null +++ b/docs/docs/tutorials/learn_more.md @@ -0,0 +1,20 @@ +--- +layout: default +title: Learn More +nav_order: 5 +parent: Tutorials +--- + +# Learn More + +## How it works + +When the HTTP request arrives at the gRPC-Gateway, it parses the JSON data into a protobuf message. It then makes a normal Go gRPC client request using the parsed protobuf message. The Go gRPC client encodes the protobuf structure into the protobuf binary format and sends it to the gRPC server. The gRPC Server handles the request and returns the response in the protobuf binary format. The Go gRPC client parses it into a protobuf message and returns it to the gRPC-Gateway, which encodes the protobuf message to JSON and returns it to the original client. + +## google.api.http + +Read more about `google.api.http` in [the source file documentation](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto). + +## HTTP and gRPC Transcoding + +Read more about HTTP and gRPC Transcoding on [AIP 127](https://google.aip.dev/127). diff --git a/docs/docs/tutorials/simple_hello_world.md b/docs/docs/tutorials/simple_hello_world.md new file mode 100644 index 00000000000..71251833251 --- /dev/null +++ b/docs/docs/tutorials/simple_hello_world.md @@ -0,0 +1,40 @@ +--- +layout: default +title: Creating a simple hello world with gRPC +nav_order: 1 +parent: Tutorials +--- + +# Creating a simple hello world with gRPC + +To understand the gRPC-Gateway we are going to first make a hello world gRPC service. + +## Defining your gRPC service using protocol buffers + +Before we create a gRPC service, we should create a proto file to define what we need, here we create a file named `hello_world.proto` in the directory `proto/helloworld/hello_world.proto`. + +The gRPC service is defined using [Google Protocol Buffers](https://developers.google.com/protocol-buffers). To learn more about how to define a service in a `.proto` file see their [Basics tutorial](https://grpc.io/docs/languages/go/basics/). For now, all you need to know is that both the server and the client stub have a `SayHello()` RPC method that takes a `HelloRequest` parameter from the client and returns a `HelloReply` from the server, and that the method is defined like this: + +```protobuf +syntax = "proto3"; + +package helloworld; + +// The greeting service definition +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} +``` + +[Next](generating_stubs/index.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 00000000000..d52f5093085 Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/index.md b/docs/index.md index 47b6ec026fc..88910e1484a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,27 +1,53 @@ -# grpc-gateway +--- +layout: default +title: gRPC-Gateway +nav_order: 0 +description: "Documentation site for the gRPC-Gateway" +permalink: / +--- -[![Build Status](https://travis-ci.org/grpc-ecosystem/grpc-gateway.svg?branch=master)](https://travis-ci.org/grpc-ecosystem/grpc-gateway) +# gRPC-Gateway +{: .fs-9 } -grpc-gateway is a plugin of [protoc](http://github.com/google/protobuf). -It reads [gRPC](http://github.com/grpc/grpc-common) service definition, -and generates a reverse-proxy server which translates a RESTful JSON API into gRPC. -This server is generated according to [custom options](https://cloud.google.com/service-management/reference/rpc/google.api#http) in your gRPC definition. +gRPC-Gateway is a plugin of [protoc](https://github.com/protocolbuffers/protobuf). It reads a [gRPC](https://grpc.io/) service definition and generates a reverse-proxy server which translates a RESTful JSON API into gRPC. This server is generated according to [custom options](https://cloud.google.com/service-infrastructure/docs/service-management/reference/rpc/google.api#http) in your gRPC definition. +{: .fs-6 .fw-300 } -It helps you to provide your APIs in both gRPC and RESTful style at the same time. +[Get started](#getting-started){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } [View it on GitHub](https://github.com/grpc-ecosystem/grpc-gateway){: .btn .fs-5 .mb-4 .mb-md-0 } -![architecture introduction diagram](https://docs.google.com/drawings/d/12hp4CPqrNPFhattL_cIoJptFvlAqm5wLQ0ggqI5mkCg/pub?w=749&h=370) +--- -To learn more about us check out our documentation on: +## Getting started -* [Our background](_docs/background.md) -* [Installation and usage](_docs/usage.md) -* [Examples](_docs/examples.md) -* [Features](_docs/features.md) + + + + + +gRPC-Gateway helps you to provide your APIs in both gRPC and RESTful style at the same time. -# Contribution -See [CONTRIBUTING.md](http://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md). +
+ +
+ +To learn more about gRPC-Gateway check out the documentation. + +## Contribution + +See [CONTRIBUTING.md](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/CONTRIBUTING.md). + +## License + +gRPC-Gateway is licensed under the BSD 3-Clause License. -# License -grpc-gateway is licensed under the BSD 3-Clause License. See [LICENSE.txt](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/LICENSE.txt) for more details. + +### Thank you to the contributors of gRPC-Gateway + +
    +{% for contributor in site.github.contributors %} +
  • +{{ contributor.login }} +
  • +{% endfor %} +
diff --git a/docs/run.sh b/docs/run.sh index b9b5a2c031b..7cfbe8f97c4 100755 --- a/docs/run.sh +++ b/docs/run.sh @@ -2,7 +2,7 @@ set -e -JEKYLL_VERSION=3.5 +JEKYLL_VERSION=4 BUNDLE_DIR="/tmp/grpc-gateway-bundle" if [ ! -d "${BUNDLE_DIR}" ]; then @@ -18,11 +18,17 @@ if [ ! -d "${BUNDLE_DIR}" ]; then bundle update fi +if [[ ${JEKYLL_GITHUB_TOKEN} == "" ]]; then + echo "Please set \$JEKYLL_GITHUB_TOKEN before running" + exit 1 +fi + docker run --rm \ --volume="${PWD}:/srv/jekyll" \ -p 35729:35729 -p 4000:4000 \ -e "JEKYLL_UID=$(id -u)" \ -e "JEKYLL_GID=$(id -g)" \ + -e "JEKYLL_GITHUB_TOKEN=${JEKYLL_GITHUB_TOKEN}" \ --volume="/tmp/grpc-gateway-bundle:/usr/local/bundle" \ -it "jekyll/builder:${JEKYLL_VERSION}" \ jekyll serve diff --git a/examples/browser/a_bit_of_everything_service.spec.js b/examples/browser/a_bit_of_everything_service.spec.js deleted file mode 100644 index edcbebe11d6..00000000000 --- a/examples/browser/a_bit_of_everything_service.spec.js +++ /dev/null @@ -1,185 +0,0 @@ -'use strict'; - -var SwaggerClient = require('swagger-client'); - -describe('ABitOfEverythingService', function() { - var client; - - beforeEach(function(done) { - new SwaggerClient({ - url: "http://localhost:8080/swagger/a_bit_of_everything.swagger.json", - usePromise: true, - }).then(function(c) { - client = c; - }).catch(function(err) { - done.fail(err); - }).then(done); - }); - - describe('Create', function() { - var created; - var expected = { - float_value: 1.5, - double_value: 2.5, - int64_value: "4294967296", - uint64_value: "9223372036854775807", - int32_value: -2147483648, - fixed64_value: "9223372036854775807", - fixed32_value: 4294967295, - bool_value: true, - string_value: "strprefix/foo", - uint32_value: 4294967295, - sfixed32_value: 2147483647, - sfixed64_value: "-4611686018427387904", - sint32_value: 2147483647, - sint64_value: "4611686018427387903", - nonConventionalNameValue: "camelCase", - }; - - beforeEach(function(done) { - client.ABitOfEverythingService.Create(expected).then(function(resp) { - created = resp.obj; - }).catch(function(err) { - done.fail(err); - }).then(done); - }); - - it('should assign id', function() { - expect(created.uuid).not.toBe(""); - }); - - it('should echo the request back', function() { - delete created.uuid; - expect(created).toEqual(expected); - }); - }); - - describe('CreateBody', function() { - var created; - var expected = { - float_value: 1.5, - double_value: 2.5, - int64_value: "4294967296", - uint64_value: "9223372036854775807", - int32_value: -2147483648, - fixed64_value: "9223372036854775807", - fixed32_value: 4294967295, - bool_value: true, - string_value: "strprefix/foo", - uint32_value: 4294967295, - sfixed32_value: 2147483647, - sfixed64_value: "-4611686018427387904", - sint32_value: 2147483647, - sint64_value: "4611686018427387903", - nonConventionalNameValue: "camelCase", - - nested: [ - { name: "bar", amount: 10 }, - { name: "baz", amount: 20 }, - ], - repeated_string_value: ["a", "b", "c"], - oneof_string: "x", - // TODO(yugui) Support enum by name - map_value: { a: 1, b: 2 }, - mapped_string_value: { a: "x", b: "y" }, - mapped_nested_value: { - a: { name: "x", amount: 1 }, - b: { name: "y", amount: 2 }, - }, - }; - - beforeEach(function(done) { - client.ABitOfEverythingService.CreateBody({ - body: expected, - }).then(function(resp) { - created = resp.obj; - }).catch(function(err) { - done.fail(err); - }).then(done); - }); - - it('should assign id', function() { - expect(created.uuid).not.toBe(""); - }); - - it('should echo the request back', function() { - delete created.uuid; - expect(created).toEqual(expected); - }); - }); - - describe('lookup', function() { - var created; - var expected = { - bool_value: true, - string_value: "strprefix/foo", - }; - - beforeEach(function(done) { - client.ABitOfEverythingService.CreateBody({ - body: expected, - }).then(function(resp) { - created = resp.obj; - }).catch(function(err) { - fail(err); - }).finally(done); - }); - - it('should look up an object by uuid', function(done) { - client.ABitOfEverythingService.Lookup({ - uuid: created.uuid - }).then(function(resp) { - expect(resp.obj).toEqual(created); - }).catch(function(err) { - fail(err.errObj); - }).finally(done); - }); - - it('should fail if no such object', function(done) { - client.ABitOfEverythingService.Lookup({ - uuid: 'not_exist', - }).then(function(resp) { - fail('expected failure but succeeded'); - }).catch(function(err) { - expect(err.status).toBe(404); - }).finally(done); - }); - }); - - describe('Delete', function() { - var created; - var expected = { - bool_value: true, - string_value: "strprefix/foo", - }; - - beforeEach(function(done) { - client.ABitOfEverythingService.CreateBody({ - body: expected, - }).then(function(resp) { - created = resp.obj; - }).catch(function(err) { - fail(err); - }).finally(done); - }); - - it('should delete an object by id', function(done) { - client.ABitOfEverythingService.Delete({ - uuid: created.uuid - }).then(function(resp) { - expect(resp.obj).toEqual({}); - }).catch(function(err) { - fail(err.errObj); - }).then(function() { - return client.ABitOfEverythingService.Lookup({ - uuid: created.uuid - }); - }).then(function(resp) { - fail('expected failure but succeeded'); - }). catch(function(err) { - expect(err.status).toBe(404); - }).finally(done); - }); - }); -}); - diff --git a/examples/browser/echo_service.spec.js b/examples/browser/echo_service.spec.js deleted file mode 100644 index 97888c3e6c7..00000000000 --- a/examples/browser/echo_service.spec.js +++ /dev/null @@ -1,43 +0,0 @@ -'use strict'; - -var SwaggerClient = require('swagger-client'); - -describe('EchoService', function() { - var client; - - beforeEach(function(done) { - new SwaggerClient({ - url: "http://localhost:8080/swagger/echo_service.swagger.json", - usePromise: true, - }).then(function(c) { - client = c; - done(); - }); - }); - - describe('Echo', function() { - it('should echo the request back', function(done) { - client.EchoService.Echo( - {id: "foo"}, - {responseContentType: "application/json"} - ).then(function(resp) { - expect(resp.obj).toEqual({id: "foo"}); - }).catch(function(err) { - done.fail(err); - }).then(done); - }); - }); - - describe('EchoBody', function() { - it('should echo the request back', function(done) { - client.EchoService.EchoBody( - {body: {id: "foo"}}, - {responseContentType: "application/json"} - ).then(function(resp) { - expect(resp.obj).toEqual({id: "foo"}); - }).catch(function(err) { - done.fail(err); - }).then(done); - }); - }); -}); diff --git a/examples/clients/abe/BUILD.bazel b/examples/clients/abe/BUILD.bazel deleted file mode 100644 index fad5b661901..00000000000 --- a/examples/clients/abe/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -package(default_visibility = ["//visibility:public"]) - -go_library( - name = "go_default_library", - srcs = [ - "a_bit_of_everything_nested.go", - "a_bit_of_everything_service_api.go", - "api_client.go", - "api_response.go", - "camel_case_service_name_api.go", - "configuration.go", - "echo_rpc_api.go", - "echo_service_api.go", - "examplepb_a_bit_of_everything.go", - "examplepb_body.go", - "examplepb_numeric_enum.go", - "nested_deep_enum.go", - "protobuf_empty.go", - "sub_string_message.go", - ], - importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/clients/abe", - deps = ["@com_github_go_resty_resty//:go_default_library"], -) diff --git a/examples/clients/abe/a_bit_of_everything_service_api.go b/examples/clients/abe/a_bit_of_everything_service_api.go deleted file mode 100644 index 19c6fdaa7dd..00000000000 --- a/examples/clients/abe/a_bit_of_everything_service_api.go +++ /dev/null @@ -1,938 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -import ( - "net/url" - "strings" - "time" - "encoding/json" - "fmt" -) - -type ABitOfEverythingServiceApi struct { - Configuration *Configuration -} - -func NewABitOfEverythingServiceApi() *ABitOfEverythingServiceApi { - configuration := NewConfiguration() - return &ABitOfEverythingServiceApi{ - Configuration: configuration, - } -} - -func NewABitOfEverythingServiceApiWithBasePath(basePath string) *ABitOfEverythingServiceApi { - configuration := NewConfiguration() - configuration.BasePath = basePath - - return &ABitOfEverythingServiceApi{ - Configuration: configuration, - } -} - -/** - * - * - * @param floatValue - * @param doubleValue - * @param int64Value - * @param uint64Value - * @param int32Value - * @param fixed64Value - * @param fixed32Value - * @param boolValue - * @param stringValue - * @param uint32Value - * @param sfixed32Value - * @param sfixed64Value - * @param sint32Value - * @param sint64Value - * @param nonConventionalNameValue - * @return *ExamplepbABitOfEverything - */ -func (a ABitOfEverythingServiceApi) Create(floatValue float32, doubleValue float64, int64Value string, uint64Value string, int32Value int32, fixed64Value string, fixed32Value int64, boolValue bool, stringValue string, uint32Value int64, sfixed32Value int32, sfixed64Value string, sint32Value int32, sint64Value string, nonConventionalNameValue string) (*ExamplepbABitOfEverything, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything/{float_value}/{double_value}/{int64_value}/separator/{uint64_value}/{int32_value}/{fixed64_value}/{fixed32_value}/{bool_value}/{string_value}/{uint32_value}/{sfixed32_value}/{sfixed64_value}/{sint32_value}/{sint64_value}/{nonConventionalNameValue}" - localVarPath = strings.Replace(localVarPath, "{"+"float_value"+"}", fmt.Sprintf("%v", floatValue), -1) - localVarPath = strings.Replace(localVarPath, "{"+"double_value"+"}", fmt.Sprintf("%v", doubleValue), -1) - localVarPath = strings.Replace(localVarPath, "{"+"int64_value"+"}", fmt.Sprintf("%v", int64Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"uint64_value"+"}", fmt.Sprintf("%v", uint64Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"int32_value"+"}", fmt.Sprintf("%v", int32Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"fixed64_value"+"}", fmt.Sprintf("%v", fixed64Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"fixed32_value"+"}", fmt.Sprintf("%v", fixed32Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"bool_value"+"}", fmt.Sprintf("%v", boolValue), -1) - localVarPath = strings.Replace(localVarPath, "{"+"string_value"+"}", fmt.Sprintf("%v", stringValue), -1) - localVarPath = strings.Replace(localVarPath, "{"+"uint32_value"+"}", fmt.Sprintf("%v", uint32Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sfixed32_value"+"}", fmt.Sprintf("%v", sfixed32Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sfixed64_value"+"}", fmt.Sprintf("%v", sfixed64Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sint32_value"+"}", fmt.Sprintf("%v", sint32Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sint64_value"+"}", fmt.Sprintf("%v", sint64Value), -1) - localVarPath = strings.Replace(localVarPath, "{"+"nonConventionalNameValue"+"}", fmt.Sprintf("%v", nonConventionalNameValue), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbABitOfEverything) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Create", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @param body - * @return *ExamplepbABitOfEverything - */ -func (a ABitOfEverythingServiceApi) CreateBody(body ExamplepbABitOfEverything) (*ExamplepbABitOfEverything, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(ExamplepbABitOfEverything) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "CreateBody", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @param singleNestedName - * @param body - * @return *ExamplepbABitOfEverything - */ -func (a ABitOfEverythingServiceApi) DeepPathEcho(singleNestedName string, body ExamplepbABitOfEverything) (*ExamplepbABitOfEverything, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything/{single_nested.name}" - localVarPath = strings.Replace(localVarPath, "{"+"single_nested.name"+"}", fmt.Sprintf("%v", singleNestedName), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(ExamplepbABitOfEverything) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "DeepPathEcho", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @param uuid - * @return *ProtobufEmpty - */ -func (a ABitOfEverythingServiceApi) Delete(uuid string) (*ProtobufEmpty, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Delete") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything/{uuid}" - localVarPath = strings.Replace(localVarPath, "{"+"uuid"+"}", fmt.Sprintf("%v", uuid), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ProtobufEmpty) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Delete", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @return *ProtobufEmpty - */ -func (a ABitOfEverythingServiceApi) ErrorWithDetails() (*ProtobufEmpty, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/errorwithdetails" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ProtobufEmpty) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "ErrorWithDetails", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @param id - * @param body - * @return *ProtobufEmpty - */ -func (a ABitOfEverythingServiceApi) GetMessageWithBody(id string, body ExamplepbBody) (*ProtobufEmpty, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/withbody/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(ProtobufEmpty) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "GetMessageWithBody", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @param uuid - * @param singleNestedName name is nested field. - * @param singleNestedAmount - * @param singleNestedOk - FALSE: FALSE is false. - TRUE: TRUE is true. - * @param floatValue - * @param doubleValue - * @param int64Value - * @param uint64Value - * @param int32Value - * @param fixed64Value - * @param fixed32Value - * @param boolValue - * @param stringValue - * @param bytesValue - * @param uint32Value - * @param enumValue - ZERO: ZERO means 0 - ONE: ONE means 1 - * @param sfixed32Value - * @param sfixed64Value - * @param sint32Value - * @param sint64Value - * @param repeatedStringValue - * @param oneofString - * @param nonConventionalNameValue - * @param timestampValue - * @param repeatedEnumValue repeated enum value. it is comma-separated in query. - ZERO: ZERO means 0 - ONE: ONE means 1 - * @return *ProtobufEmpty - */ -func (a ABitOfEverythingServiceApi) GetQuery(uuid string, singleNestedName string, singleNestedAmount int64, singleNestedOk string, floatValue float32, doubleValue float64, int64Value string, uint64Value string, int32Value int32, fixed64Value string, fixed32Value int64, boolValue bool, stringValue string, bytesValue string, uint32Value int64, enumValue string, sfixed32Value int32, sfixed64Value string, sint32Value int32, sint64Value string, repeatedStringValue []string, oneofString string, nonConventionalNameValue string, timestampValue time.Time, repeatedEnumValue []string) (*ProtobufEmpty, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything/query/{uuid}" - localVarPath = strings.Replace(localVarPath, "{"+"uuid"+"}", fmt.Sprintf("%v", uuid), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("single_nested.name", a.Configuration.APIClient.ParameterToString(singleNestedName, "")) - localVarQueryParams.Add("single_nested.amount", a.Configuration.APIClient.ParameterToString(singleNestedAmount, "")) - localVarQueryParams.Add("single_nested.ok", a.Configuration.APIClient.ParameterToString(singleNestedOk, "")) - localVarQueryParams.Add("float_value", a.Configuration.APIClient.ParameterToString(floatValue, "")) - localVarQueryParams.Add("double_value", a.Configuration.APIClient.ParameterToString(doubleValue, "")) - localVarQueryParams.Add("int64_value", a.Configuration.APIClient.ParameterToString(int64Value, "")) - localVarQueryParams.Add("uint64_value", a.Configuration.APIClient.ParameterToString(uint64Value, "")) - localVarQueryParams.Add("int32_value", a.Configuration.APIClient.ParameterToString(int32Value, "")) - localVarQueryParams.Add("fixed64_value", a.Configuration.APIClient.ParameterToString(fixed64Value, "")) - localVarQueryParams.Add("fixed32_value", a.Configuration.APIClient.ParameterToString(fixed32Value, "")) - localVarQueryParams.Add("bool_value", a.Configuration.APIClient.ParameterToString(boolValue, "")) - localVarQueryParams.Add("string_value", a.Configuration.APIClient.ParameterToString(stringValue, "")) - localVarQueryParams.Add("bytes_value", a.Configuration.APIClient.ParameterToString(bytesValue, "")) - localVarQueryParams.Add("uint32_value", a.Configuration.APIClient.ParameterToString(uint32Value, "")) - localVarQueryParams.Add("enum_value", a.Configuration.APIClient.ParameterToString(enumValue, "")) - localVarQueryParams.Add("sfixed32_value", a.Configuration.APIClient.ParameterToString(sfixed32Value, "")) - localVarQueryParams.Add("sfixed64_value", a.Configuration.APIClient.ParameterToString(sfixed64Value, "")) - localVarQueryParams.Add("sint32_value", a.Configuration.APIClient.ParameterToString(sint32Value, "")) - localVarQueryParams.Add("sint64_value", a.Configuration.APIClient.ParameterToString(sint64Value, "")) - var repeatedStringValueCollectionFormat = "csv" - localVarQueryParams.Add("repeated_string_value", a.Configuration.APIClient.ParameterToString(repeatedStringValue, repeatedStringValueCollectionFormat)) - - localVarQueryParams.Add("oneof_string", a.Configuration.APIClient.ParameterToString(oneofString, "")) - localVarQueryParams.Add("nonConventionalNameValue", a.Configuration.APIClient.ParameterToString(nonConventionalNameValue, "")) - localVarQueryParams.Add("timestamp_value", a.Configuration.APIClient.ParameterToString(timestampValue, "")) - var repeatedEnumValueCollectionFormat = "csv" - localVarQueryParams.Add("repeated_enum_value", a.Configuration.APIClient.ParameterToString(repeatedEnumValue, repeatedEnumValueCollectionFormat)) - - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ProtobufEmpty) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "GetQuery", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @param uuid - * @return *ExamplepbABitOfEverything - */ -func (a ABitOfEverythingServiceApi) Lookup(uuid string) (*ExamplepbABitOfEverything, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything/{uuid}" - localVarPath = strings.Replace(localVarPath, "{"+"uuid"+"}", fmt.Sprintf("%v", uuid), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbABitOfEverything) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Lookup", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @param name - * @param body - * @return *ProtobufEmpty - */ -func (a ABitOfEverythingServiceApi) PostWithEmptyBody(name string, body ExamplepbBody) (*ProtobufEmpty, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/postwithemptybody/{name}" - localVarPath = strings.Replace(localVarPath, "{"+"name"+"}", fmt.Sprintf("%v", name), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(ProtobufEmpty) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "PostWithEmptyBody", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @return *ProtobufEmpty - */ -func (a ABitOfEverythingServiceApi) Timeout() (*ProtobufEmpty, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/timeout" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ProtobufEmpty) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Timeout", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * - * - * @param uuid - * @param body - * @return *ProtobufEmpty - */ -func (a ABitOfEverythingServiceApi) Update(uuid string, body ExamplepbABitOfEverything) (*ProtobufEmpty, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Put") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything/{uuid}" - localVarPath = strings.Replace(localVarPath, "{"+"uuid"+"}", fmt.Sprintf("%v", uuid), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(ProtobufEmpty) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Update", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - diff --git a/examples/clients/abe/api_client.go b/examples/clients/abe/api_client.go deleted file mode 100644 index bf3e21a9fb1..00000000000 --- a/examples/clients/abe/api_client.go +++ /dev/null @@ -1,164 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -import ( - "bytes" - "fmt" - "path/filepath" - "reflect" - "strings" - "net/url" - "io/ioutil" - "github.com/go-resty/resty" -) - -type APIClient struct { - config *Configuration -} - -func (c *APIClient) SelectHeaderContentType(contentTypes []string) string { - - if len(contentTypes) == 0 { - return "" - } - if contains(contentTypes, "application/json") { - return "application/json" - } - return contentTypes[0] // use the first content type specified in 'consumes' -} - -func (c *APIClient) SelectHeaderAccept(accepts []string) string { - - if len(accepts) == 0 { - return "" - } - if contains(accepts, "application/json") { - return "application/json" - } - return strings.Join(accepts, ",") -} - -func contains(haystack []string, needle string) bool { - for _, a := range haystack { - if strings.ToLower(a) == strings.ToLower(needle) { - return true - } - } - return false -} - -func (c *APIClient) CallAPI(path string, method string, - postBody interface{}, - headerParams map[string]string, - queryParams url.Values, - formParams map[string]string, - fileName string, - fileBytes []byte) (*resty.Response, error) { - - rClient := c.prepareClient() - request := c.prepareRequest(rClient, postBody, headerParams, queryParams, formParams, fileName, fileBytes) - - switch strings.ToUpper(method) { - case "GET": - response, err := request.Get(path) - return response, err - case "POST": - response, err := request.Post(path) - return response, err - case "PUT": - response, err := request.Put(path) - return response, err - case "PATCH": - response, err := request.Patch(path) - return response, err - case "DELETE": - response, err := request.Delete(path) - return response, err - } - - return nil, fmt.Errorf("invalid method %v", method) -} - -func (c *APIClient) ParameterToString(obj interface{}, collectionFormat string) string { - delimiter := "" - switch collectionFormat { - case "pipes": - delimiter = "|" - case "ssv": - delimiter = " " - case "tsv": - delimiter = "\t" - case "csv": - delimiter = "," - } - - if reflect.TypeOf(obj).Kind() == reflect.Slice { - return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") - } - - return fmt.Sprintf("%v", obj) -} - -func (c *APIClient) prepareClient() *resty.Client { - - rClient := resty.New() - - rClient.SetDebug(c.config.Debug) - if c.config.Transport != nil { - rClient.SetTransport(c.config.Transport) - } - - if c.config.Timeout != nil { - rClient.SetTimeout(*c.config.Timeout) - } - rClient.SetLogger(ioutil.Discard) - return rClient -} - -func (c *APIClient) prepareRequest( - rClient *resty.Client, - postBody interface{}, - headerParams map[string]string, - queryParams url.Values, - formParams map[string]string, - fileName string, - fileBytes []byte) *resty.Request { - - - request := rClient.R() - request.SetBody(postBody) - - if c.config.UserAgent != "" { - request.SetHeader("User-Agent", c.config.UserAgent) - } - - // add header parameter, if any - if len(headerParams) > 0 { - request.SetHeaders(headerParams) - } - - // add query parameter, if any - if len(queryParams) > 0 { - request.SetMultiValueQueryParams(queryParams) - } - - // add form parameter, if any - if len(formParams) > 0 { - request.SetFormData(formParams) - } - - if len(fileBytes) > 0 && fileName != "" { - _, fileNm := filepath.Split(fileName) - request.SetFileReader("file", fileNm, bytes.NewReader(fileBytes)) - } - return request -} diff --git a/examples/clients/abe/camel_case_service_name_api.go b/examples/clients/abe/camel_case_service_name_api.go deleted file mode 100644 index 43c92f74152..00000000000 --- a/examples/clients/abe/camel_case_service_name_api.go +++ /dev/null @@ -1,110 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -import ( - "net/url" - "strings" - "encoding/json" -) - -type CamelCaseServiceNameApi struct { - Configuration *Configuration -} - -func NewCamelCaseServiceNameApi() *CamelCaseServiceNameApi { - configuration := NewConfiguration() - return &CamelCaseServiceNameApi{ - Configuration: configuration, - } -} - -func NewCamelCaseServiceNameApiWithBasePath(basePath string) *CamelCaseServiceNameApi { - configuration := NewConfiguration() - configuration.BasePath = basePath - - return &CamelCaseServiceNameApi{ - Configuration: configuration, - } -} - -/** - * - * - * @return *ProtobufEmpty - */ -func (a CamelCaseServiceNameApi) Empty() (*ProtobufEmpty, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/empty" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ProtobufEmpty) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Empty", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - diff --git a/examples/clients/abe/configuration.go b/examples/clients/abe/configuration.go deleted file mode 100644 index ccc319c34aa..00000000000 --- a/examples/clients/abe/configuration.go +++ /dev/null @@ -1,67 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -import ( - "encoding/base64" - "net/http" - "time" -) - - -type Configuration struct { - Username string `json:"userName,omitempty"` - Password string `json:"password,omitempty"` - APIKeyPrefix map[string]string `json:"APIKeyPrefix,omitempty"` - APIKey map[string]string `json:"APIKey,omitempty"` - Debug bool `json:"debug,omitempty"` - DebugFile string `json:"debugFile,omitempty"` - OAuthToken string `json:"oAuthToken,omitempty"` - BasePath string `json:"basePath,omitempty"` - Host string `json:"host,omitempty"` - Scheme string `json:"scheme,omitempty"` - AccessToken string `json:"accessToken,omitempty"` - DefaultHeader map[string]string `json:"defaultHeader,omitempty"` - UserAgent string `json:"userAgent,omitempty"` - APIClient *APIClient - Transport *http.Transport - Timeout *time.Duration `json:"timeout,omitempty"` -} - -func NewConfiguration() *Configuration { - cfg := &Configuration{ - BasePath: "http://localhost", - DefaultHeader: make(map[string]string), - APIKey: make(map[string]string), - APIKeyPrefix: make(map[string]string), - UserAgent: "Swagger-Codegen/1.0.0/go", - APIClient: &APIClient{}, - } - - cfg.APIClient.config = cfg - return cfg -} - -func (c *Configuration) GetBasicAuthEncodedString() string { - return base64.StdEncoding.EncodeToString([]byte(c.Username + ":" + c.Password)) -} - -func (c *Configuration) AddDefaultHeader(key string, value string) { - c.DefaultHeader[key] = value -} - -func (c *Configuration) GetAPIKeyWithPrefix(APIKeyIdentifier string) string { - if c.APIKeyPrefix[APIKeyIdentifier] != "" { - return c.APIKeyPrefix[APIKeyIdentifier] + " " + c.APIKey[APIKeyIdentifier] - } - - return c.APIKey[APIKeyIdentifier] -} diff --git a/examples/clients/abe/echo_rpc_api.go b/examples/clients/abe/echo_rpc_api.go deleted file mode 100644 index 8afbbdd437f..00000000000 --- a/examples/clients/abe/echo_rpc_api.go +++ /dev/null @@ -1,265 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -import ( - "net/url" - "strings" - "encoding/json" - "fmt" -) - -type EchoRpcApi struct { - Configuration *Configuration -} - -func NewEchoRpcApi() *EchoRpcApi { - configuration := NewConfiguration() - return &EchoRpcApi{ - Configuration: configuration, - } -} - -func NewEchoRpcApiWithBasePath(basePath string) *EchoRpcApi { - configuration := NewConfiguration() - configuration.BasePath = basePath - - return &EchoRpcApi{ - Configuration: configuration, - } -} - -/** - * Summary: Echo rpc - * Description Echo - * - * @param value - * @return *SubStringMessage - */ -func (a EchoRpcApi) Echo(value string) (*SubStringMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything/echo/{value}" - localVarPath = strings.Replace(localVarPath, "{"+"value"+"}", fmt.Sprintf("%v", value), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(SubStringMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Summary: Echo rpc - * Description Echo - * - * @param body - * @return *SubStringMessage - */ -func (a EchoRpcApi) Echo2(body string) (*SubStringMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/echo" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(SubStringMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo2", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Summary: Echo rpc - * Description Echo - * - * @param value - * @return *SubStringMessage - */ -func (a EchoRpcApi) Echo3(value string) (*SubStringMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/echo" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("value", a.Configuration.APIClient.ParameterToString(value, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(SubStringMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo3", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - diff --git a/examples/clients/abe/echo_service_api.go b/examples/clients/abe/echo_service_api.go deleted file mode 100644 index 568177715d2..00000000000 --- a/examples/clients/abe/echo_service_api.go +++ /dev/null @@ -1,265 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -import ( - "net/url" - "strings" - "encoding/json" - "fmt" -) - -type EchoServiceApi struct { - Configuration *Configuration -} - -func NewEchoServiceApi() *EchoServiceApi { - configuration := NewConfiguration() - return &EchoServiceApi{ - Configuration: configuration, - } -} - -func NewEchoServiceApiWithBasePath(basePath string) *EchoServiceApi { - configuration := NewConfiguration() - configuration.BasePath = basePath - - return &EchoServiceApi{ - Configuration: configuration, - } -} - -/** - * Summary: Echo rpc - * Description Echo - * - * @param value - * @return *SubStringMessage - */ -func (a EchoServiceApi) Echo(value string) (*SubStringMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything/echo/{value}" - localVarPath = strings.Replace(localVarPath, "{"+"value"+"}", fmt.Sprintf("%v", value), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(SubStringMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Summary: Echo rpc - * Description Echo - * - * @param body - * @return *SubStringMessage - */ -func (a EchoServiceApi) Echo2(body string) (*SubStringMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/echo" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(SubStringMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo2", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Summary: Echo rpc - * Description Echo - * - * @param value - * @return *SubStringMessage - */ -func (a EchoServiceApi) Echo3(value string) (*SubStringMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v2/example/echo" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // authentication '(OAuth2)' required - // oauth required - if a.Configuration.AccessToken != ""{ - localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken - } - // authentication '(BasicAuth)' required - // http basic authentication required - if a.Configuration.Username != "" || a.Configuration.Password != ""{ - localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString() - } - // authentication '(ApiKeyAuth)' required - // set key with prefix in header - localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key") - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("value", a.Configuration.APIClient.ParameterToString(value, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - "application/x-foo-mime", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(SubStringMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo3", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - diff --git a/examples/clients/abe/examplepb_a_bit_of_everything.go b/examples/clients/abe/examplepb_a_bit_of_everything.go deleted file mode 100644 index cb6fb3a487d..00000000000 --- a/examples/clients/abe/examplepb_a_bit_of_everything.go +++ /dev/null @@ -1,74 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -import ( - "time" -) - -type ExamplepbABitOfEverything struct { - - SingleNested ABitOfEverythingNested `json:"single_nested,omitempty"` - - Uuid string `json:"uuid,omitempty"` - - Nested []ABitOfEverythingNested `json:"nested,omitempty"` - - FloatValue float32 `json:"float_value,omitempty"` - - DoubleValue float64 `json:"double_value,omitempty"` - - Int64Value string `json:"int64_value,omitempty"` - - Uint64Value string `json:"uint64_value,omitempty"` - - Int32Value int32 `json:"int32_value,omitempty"` - - Fixed64Value string `json:"fixed64_value,omitempty"` - - Fixed32Value int64 `json:"fixed32_value,omitempty"` - - BoolValue bool `json:"bool_value,omitempty"` - - StringValue string `json:"string_value,omitempty"` - - BytesValue string `json:"bytes_value,omitempty"` - - Uint32Value int64 `json:"uint32_value,omitempty"` - - EnumValue ExamplepbNumericEnum `json:"enum_value,omitempty"` - - Sfixed32Value int32 `json:"sfixed32_value,omitempty"` - - Sfixed64Value string `json:"sfixed64_value,omitempty"` - - Sint32Value int32 `json:"sint32_value,omitempty"` - - Sint64Value string `json:"sint64_value,omitempty"` - - RepeatedStringValue []string `json:"repeated_string_value,omitempty"` - - OneofEmpty ProtobufEmpty `json:"oneof_empty,omitempty"` - - OneofString string `json:"oneof_string,omitempty"` - - MapValue map[string]ExamplepbNumericEnum `json:"map_value,omitempty"` - - MappedStringValue map[string]string `json:"mapped_string_value,omitempty"` - - MappedNestedValue map[string]ABitOfEverythingNested `json:"mapped_nested_value,omitempty"` - - NonConventionalNameValue string `json:"nonConventionalNameValue,omitempty"` - - TimestampValue time.Time `json:"timestamp_value,omitempty"` - - RepeatedEnumValue []ExamplepbNumericEnum `json:"repeated_enum_value,omitempty"` -} diff --git a/examples/clients/abe/examplepb_numeric_enum.go b/examples/clients/abe/examplepb_numeric_enum.go deleted file mode 100644 index e953bbe34e8..00000000000 --- a/examples/clients/abe/examplepb_numeric_enum.go +++ /dev/null @@ -1,15 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -// NumericEnum is one or zero. - ZERO: ZERO means 0 - ONE: ONE means 1 -type ExamplepbNumericEnum struct { -} diff --git a/examples/clients/abe/nested_deep_enum.go b/examples/clients/abe/nested_deep_enum.go deleted file mode 100644 index e5fc17d50a4..00000000000 --- a/examples/clients/abe/nested_deep_enum.go +++ /dev/null @@ -1,15 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -// DeepEnum is one or zero. - FALSE: FALSE is false. - TRUE: TRUE is true. -type NestedDeepEnum struct { -} diff --git a/examples/clients/abe/protobuf_empty.go b/examples/clients/abe/protobuf_empty.go deleted file mode 100644 index 97c7bf612b8..00000000000 --- a/examples/clients/abe/protobuf_empty.go +++ /dev/null @@ -1,15 +0,0 @@ -/* - * A Bit of Everything - * - * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) - * - * OpenAPI spec version: 1.0 - * Contact: none@example.com - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package abe - -// service Foo { rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); } The JSON representation for `Empty` is empty JSON object `{}`. -type ProtobufEmpty struct { -} diff --git a/examples/clients/echo/BUILD.bazel b/examples/clients/echo/BUILD.bazel deleted file mode 100644 index 090b56aafab..00000000000 --- a/examples/clients/echo/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -package(default_visibility = ["//visibility:public"]) - -go_library( - name = "go_default_library", - srcs = [ - "api_client.go", - "api_response.go", - "configuration.go", - "echo_service_api.go", - "examplepb_embedded.go", - "examplepb_simple_message.go", - ], - importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/clients/echo", - deps = ["@com_github_go_resty_resty//:go_default_library"], -) diff --git a/examples/clients/echo/api_client.go b/examples/clients/echo/api_client.go deleted file mode 100644 index 7a517148026..00000000000 --- a/examples/clients/echo/api_client.go +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Echo Service - * - * Echo Service API consists of a single service which returns a message. - * - * OpenAPI spec version: version not set - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package echo - -import ( - "bytes" - "fmt" - "path/filepath" - "reflect" - "strings" - "net/url" - "io/ioutil" - "github.com/go-resty/resty" -) - -type APIClient struct { - config *Configuration -} - -func (c *APIClient) SelectHeaderContentType(contentTypes []string) string { - - if len(contentTypes) == 0 { - return "" - } - if contains(contentTypes, "application/json") { - return "application/json" - } - return contentTypes[0] // use the first content type specified in 'consumes' -} - -func (c *APIClient) SelectHeaderAccept(accepts []string) string { - - if len(accepts) == 0 { - return "" - } - if contains(accepts, "application/json") { - return "application/json" - } - return strings.Join(accepts, ",") -} - -func contains(haystack []string, needle string) bool { - for _, a := range haystack { - if strings.ToLower(a) == strings.ToLower(needle) { - return true - } - } - return false -} - -func (c *APIClient) CallAPI(path string, method string, - postBody interface{}, - headerParams map[string]string, - queryParams url.Values, - formParams map[string]string, - fileName string, - fileBytes []byte) (*resty.Response, error) { - - rClient := c.prepareClient() - request := c.prepareRequest(rClient, postBody, headerParams, queryParams, formParams, fileName, fileBytes) - - switch strings.ToUpper(method) { - case "GET": - response, err := request.Get(path) - return response, err - case "POST": - response, err := request.Post(path) - return response, err - case "PUT": - response, err := request.Put(path) - return response, err - case "PATCH": - response, err := request.Patch(path) - return response, err - case "DELETE": - response, err := request.Delete(path) - return response, err - } - - return nil, fmt.Errorf("invalid method %v", method) -} - -func (c *APIClient) ParameterToString(obj interface{}, collectionFormat string) string { - delimiter := "" - switch collectionFormat { - case "pipes": - delimiter = "|" - case "ssv": - delimiter = " " - case "tsv": - delimiter = "\t" - case "csv": - delimiter = "," - } - - if reflect.TypeOf(obj).Kind() == reflect.Slice { - return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") - } - - return fmt.Sprintf("%v", obj) -} - -func (c *APIClient) prepareClient() *resty.Client { - - rClient := resty.New() - - rClient.SetDebug(c.config.Debug) - if c.config.Transport != nil { - rClient.SetTransport(c.config.Transport) - } - - if c.config.Timeout != nil { - rClient.SetTimeout(*c.config.Timeout) - } - rClient.SetLogger(ioutil.Discard) - return rClient -} - -func (c *APIClient) prepareRequest( - rClient *resty.Client, - postBody interface{}, - headerParams map[string]string, - queryParams url.Values, - formParams map[string]string, - fileName string, - fileBytes []byte) *resty.Request { - - - request := rClient.R() - request.SetBody(postBody) - - if c.config.UserAgent != "" { - request.SetHeader("User-Agent", c.config.UserAgent) - } - - // add header parameter, if any - if len(headerParams) > 0 { - request.SetHeaders(headerParams) - } - - // add query parameter, if any - if len(queryParams) > 0 { - request.SetMultiValueQueryParams(queryParams) - } - - // add form parameter, if any - if len(formParams) > 0 { - request.SetFormData(formParams) - } - - if len(fileBytes) > 0 && fileName != "" { - _, fileNm := filepath.Split(fileName) - request.SetFileReader("file", fileNm, bytes.NewReader(fileBytes)) - } - return request -} diff --git a/examples/clients/echo/configuration.go b/examples/clients/echo/configuration.go deleted file mode 100644 index 9a75a30aeec..00000000000 --- a/examples/clients/echo/configuration.go +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Echo Service - * - * Echo Service API consists of a single service which returns a message. - * - * OpenAPI spec version: version not set - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package echo - -import ( - "encoding/base64" - "net/http" - "time" -) - - -type Configuration struct { - Username string `json:"userName,omitempty"` - Password string `json:"password,omitempty"` - APIKeyPrefix map[string]string `json:"APIKeyPrefix,omitempty"` - APIKey map[string]string `json:"APIKey,omitempty"` - Debug bool `json:"debug,omitempty"` - DebugFile string `json:"debugFile,omitempty"` - OAuthToken string `json:"oAuthToken,omitempty"` - BasePath string `json:"basePath,omitempty"` - Host string `json:"host,omitempty"` - Scheme string `json:"scheme,omitempty"` - AccessToken string `json:"accessToken,omitempty"` - DefaultHeader map[string]string `json:"defaultHeader,omitempty"` - UserAgent string `json:"userAgent,omitempty"` - APIClient *APIClient - Transport *http.Transport - Timeout *time.Duration `json:"timeout,omitempty"` -} - -func NewConfiguration() *Configuration { - cfg := &Configuration{ - BasePath: "http://localhost", - DefaultHeader: make(map[string]string), - APIKey: make(map[string]string), - APIKeyPrefix: make(map[string]string), - UserAgent: "Swagger-Codegen/1.0.0/go", - APIClient: &APIClient{}, - } - - cfg.APIClient.config = cfg - return cfg -} - -func (c *Configuration) GetBasicAuthEncodedString() string { - return base64.StdEncoding.EncodeToString([]byte(c.Username + ":" + c.Password)) -} - -func (c *Configuration) AddDefaultHeader(key string, value string) { - c.DefaultHeader[key] = value -} - -func (c *Configuration) GetAPIKeyWithPrefix(APIKeyIdentifier string) string { - if c.APIKeyPrefix[APIKeyIdentifier] != "" { - return c.APIKeyPrefix[APIKeyIdentifier] + " " + c.APIKey[APIKeyIdentifier] - } - - return c.APIKey[APIKeyIdentifier] -} diff --git a/examples/clients/echo/echo_service_api.go b/examples/clients/echo/echo_service_api.go deleted file mode 100644 index 775d0b3a7e6..00000000000 --- a/examples/clients/echo/echo_service_api.go +++ /dev/null @@ -1,541 +0,0 @@ -/* - * Echo Service - * - * Echo Service API consists of a single service which returns a message. - * - * OpenAPI spec version: version not set - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package echo - -import ( - "net/url" - "strings" - "encoding/json" - "fmt" -) - -type EchoServiceApi struct { - Configuration *Configuration -} - -func NewEchoServiceApi() *EchoServiceApi { - configuration := NewConfiguration() - return &EchoServiceApi{ - Configuration: configuration, - } -} - -func NewEchoServiceApiWithBasePath(basePath string) *EchoServiceApi { - configuration := NewConfiguration() - configuration.BasePath = basePath - - return &EchoServiceApi{ - Configuration: configuration, - } -} - -/** - * Echo method receives a simple message and returns it. - * The message posted as the id parameter will also be returned. - * - * @param id - * @return *ExamplepbSimpleMessage - */ -func (a EchoServiceApi) Echo(id string) (*ExamplepbSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Echo method receives a simple message and returns it. - * The message posted as the id parameter will also be returned. - * - * @param id - * @param num - * @param lineNum - * @param lang - * @param statusProgress - * @param statusNote - * @param en - * @param noProgress - * @param noNote - * @return *ExamplepbSimpleMessage - */ -func (a EchoServiceApi) Echo2(id string, num string, lineNum string, lang string, statusProgress string, statusNote string, en string, noProgress string, noNote string) (*ExamplepbSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo/{id}/{num}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1) - localVarPath = strings.Replace(localVarPath, "{"+"num"+"}", fmt.Sprintf("%v", num), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("line_num", a.Configuration.APIClient.ParameterToString(lineNum, "")) - localVarQueryParams.Add("lang", a.Configuration.APIClient.ParameterToString(lang, "")) - localVarQueryParams.Add("status.progress", a.Configuration.APIClient.ParameterToString(statusProgress, "")) - localVarQueryParams.Add("status.note", a.Configuration.APIClient.ParameterToString(statusNote, "")) - localVarQueryParams.Add("en", a.Configuration.APIClient.ParameterToString(en, "")) - localVarQueryParams.Add("no.progress", a.Configuration.APIClient.ParameterToString(noProgress, "")) - localVarQueryParams.Add("no.note", a.Configuration.APIClient.ParameterToString(noNote, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo2", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Echo method receives a simple message and returns it. - * The message posted as the id parameter will also be returned. - * - * @param id - * @param num - * @param lang - * @param lineNum - * @param statusProgress - * @param statusNote - * @param en - * @param noProgress - * @param noNote - * @return *ExamplepbSimpleMessage - */ -func (a EchoServiceApi) Echo3(id string, num string, lang string, lineNum string, statusProgress string, statusNote string, en string, noProgress string, noNote string) (*ExamplepbSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo/{id}/{num}/{lang}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1) - localVarPath = strings.Replace(localVarPath, "{"+"num"+"}", fmt.Sprintf("%v", num), -1) - localVarPath = strings.Replace(localVarPath, "{"+"lang"+"}", fmt.Sprintf("%v", lang), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("line_num", a.Configuration.APIClient.ParameterToString(lineNum, "")) - localVarQueryParams.Add("status.progress", a.Configuration.APIClient.ParameterToString(statusProgress, "")) - localVarQueryParams.Add("status.note", a.Configuration.APIClient.ParameterToString(statusNote, "")) - localVarQueryParams.Add("en", a.Configuration.APIClient.ParameterToString(en, "")) - localVarQueryParams.Add("no.progress", a.Configuration.APIClient.ParameterToString(noProgress, "")) - localVarQueryParams.Add("no.note", a.Configuration.APIClient.ParameterToString(noNote, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo3", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Echo method receives a simple message and returns it. - * The message posted as the id parameter will also be returned. - * - * @param id - * @param lineNum - * @param statusNote - * @param num - * @param lang - * @param statusProgress - * @param en - * @param noProgress - * @return *ExamplepbSimpleMessage - */ -func (a EchoServiceApi) Echo4(id string, lineNum string, statusNote string, num string, lang string, statusProgress string, en string, noProgress string) (*ExamplepbSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo1/{id}/{line_num}/{status.note}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1) - localVarPath = strings.Replace(localVarPath, "{"+"line_num"+"}", fmt.Sprintf("%v", lineNum), -1) - localVarPath = strings.Replace(localVarPath, "{"+"status.note"+"}", fmt.Sprintf("%v", statusNote), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("num", a.Configuration.APIClient.ParameterToString(num, "")) - localVarQueryParams.Add("lang", a.Configuration.APIClient.ParameterToString(lang, "")) - localVarQueryParams.Add("status.progress", a.Configuration.APIClient.ParameterToString(statusProgress, "")) - localVarQueryParams.Add("en", a.Configuration.APIClient.ParameterToString(en, "")) - localVarQueryParams.Add("no.progress", a.Configuration.APIClient.ParameterToString(noProgress, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo4", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Echo method receives a simple message and returns it. - * The message posted as the id parameter will also be returned. - * - * @param noNote - * @param id Id represents the message identifier. - * @param num - * @param lineNum - * @param lang - * @param statusProgress - * @param en - * @param noProgress - * @return *ExamplepbSimpleMessage - */ -func (a EchoServiceApi) Echo5(noNote string, id string, num string, lineNum string, lang string, statusProgress string, en string, noProgress string) (*ExamplepbSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo2/{no.note}" - localVarPath = strings.Replace(localVarPath, "{"+"no.note"+"}", fmt.Sprintf("%v", noNote), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("id", a.Configuration.APIClient.ParameterToString(id, "")) - localVarQueryParams.Add("num", a.Configuration.APIClient.ParameterToString(num, "")) - localVarQueryParams.Add("line_num", a.Configuration.APIClient.ParameterToString(lineNum, "")) - localVarQueryParams.Add("lang", a.Configuration.APIClient.ParameterToString(lang, "")) - localVarQueryParams.Add("status.progress", a.Configuration.APIClient.ParameterToString(statusProgress, "")) - localVarQueryParams.Add("en", a.Configuration.APIClient.ParameterToString(en, "")) - localVarQueryParams.Add("no.progress", a.Configuration.APIClient.ParameterToString(noProgress, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo5", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * EchoBody method receives a simple message and returns it. - * - * @param body - * @return *ExamplepbSimpleMessage - */ -func (a EchoServiceApi) EchoBody(body ExamplepbSimpleMessage) (*ExamplepbSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo_body" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(ExamplepbSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "EchoBody", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * EchoDelete method receives a simple message and returns it. - * - * @param id Id represents the message identifier. - * @param num - * @param lineNum - * @param lang - * @param statusProgress - * @param statusNote - * @param en - * @param noProgress - * @param noNote - * @return *ExamplepbSimpleMessage - */ -func (a EchoServiceApi) EchoDelete(id string, num string, lineNum string, lang string, statusProgress string, statusNote string, en string, noProgress string, noNote string) (*ExamplepbSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Delete") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo_delete" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("id", a.Configuration.APIClient.ParameterToString(id, "")) - localVarQueryParams.Add("num", a.Configuration.APIClient.ParameterToString(num, "")) - localVarQueryParams.Add("line_num", a.Configuration.APIClient.ParameterToString(lineNum, "")) - localVarQueryParams.Add("lang", a.Configuration.APIClient.ParameterToString(lang, "")) - localVarQueryParams.Add("status.progress", a.Configuration.APIClient.ParameterToString(statusProgress, "")) - localVarQueryParams.Add("status.note", a.Configuration.APIClient.ParameterToString(statusNote, "")) - localVarQueryParams.Add("en", a.Configuration.APIClient.ParameterToString(en, "")) - localVarQueryParams.Add("no.progress", a.Configuration.APIClient.ParameterToString(noProgress, "")) - localVarQueryParams.Add("no.note", a.Configuration.APIClient.ParameterToString(noNote, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "EchoDelete", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - diff --git a/examples/clients/unannotatedecho/BUILD.bazel b/examples/clients/unannotatedecho/BUILD.bazel deleted file mode 100644 index 611c6c7052d..00000000000 --- a/examples/clients/unannotatedecho/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -package(default_visibility = ["//visibility:public"]) - -go_library( - name = "go_default_library", - srcs = [ - "api_client.go", - "api_response.go", - "configuration.go", - "examplepb_unannotated_simple_message.go", - "unannotated_echo_service_api.go", - ], - importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/clients/unannotatedecho", - deps = ["@com_github_go_resty_resty//:go_default_library"], -) diff --git a/examples/clients/unannotatedecho/api_client.go b/examples/clients/unannotatedecho/api_client.go deleted file mode 100644 index aa4c1f14e42..00000000000 --- a/examples/clients/unannotatedecho/api_client.go +++ /dev/null @@ -1,164 +0,0 @@ -/* - * examples/proto/examplepb/unannotated_echo_service.proto - * - * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format. Echo Service API consists of a single service which returns a message. - * - * OpenAPI spec version: version not set - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package unannotatedecho - -import ( - "bytes" - "fmt" - "path/filepath" - "reflect" - "strings" - "net/url" - "io/ioutil" - "github.com/go-resty/resty" -) - -type APIClient struct { - config *Configuration -} - -func (c *APIClient) SelectHeaderContentType(contentTypes []string) string { - - if len(contentTypes) == 0 { - return "" - } - if contains(contentTypes, "application/json") { - return "application/json" - } - return contentTypes[0] // use the first content type specified in 'consumes' -} - -func (c *APIClient) SelectHeaderAccept(accepts []string) string { - - if len(accepts) == 0 { - return "" - } - if contains(accepts, "application/json") { - return "application/json" - } - return strings.Join(accepts, ",") -} - -func contains(haystack []string, needle string) bool { - for _, a := range haystack { - if strings.ToLower(a) == strings.ToLower(needle) { - return true - } - } - return false -} - -func (c *APIClient) CallAPI(path string, method string, - postBody interface{}, - headerParams map[string]string, - queryParams url.Values, - formParams map[string]string, - fileName string, - fileBytes []byte) (*resty.Response, error) { - - rClient := c.prepareClient() - request := c.prepareRequest(rClient, postBody, headerParams, queryParams, formParams, fileName, fileBytes) - - switch strings.ToUpper(method) { - case "GET": - response, err := request.Get(path) - return response, err - case "POST": - response, err := request.Post(path) - return response, err - case "PUT": - response, err := request.Put(path) - return response, err - case "PATCH": - response, err := request.Patch(path) - return response, err - case "DELETE": - response, err := request.Delete(path) - return response, err - } - - return nil, fmt.Errorf("invalid method %v", method) -} - -func (c *APIClient) ParameterToString(obj interface{}, collectionFormat string) string { - delimiter := "" - switch collectionFormat { - case "pipes": - delimiter = "|" - case "ssv": - delimiter = " " - case "tsv": - delimiter = "\t" - case "csv": - delimiter = "," - } - - if reflect.TypeOf(obj).Kind() == reflect.Slice { - return strings.Trim(strings.Replace(fmt.Sprint(obj), " ", delimiter, -1), "[]") - } - - return fmt.Sprintf("%v", obj) -} - -func (c *APIClient) prepareClient() *resty.Client { - - rClient := resty.New() - - rClient.SetDebug(c.config.Debug) - if c.config.Transport != nil { - rClient.SetTransport(c.config.Transport) - } - - if c.config.Timeout != nil { - rClient.SetTimeout(*c.config.Timeout) - } - rClient.SetLogger(ioutil.Discard) - return rClient -} - -func (c *APIClient) prepareRequest( - rClient *resty.Client, - postBody interface{}, - headerParams map[string]string, - queryParams url.Values, - formParams map[string]string, - fileName string, - fileBytes []byte) *resty.Request { - - - request := rClient.R() - request.SetBody(postBody) - - if c.config.UserAgent != "" { - request.SetHeader("User-Agent", c.config.UserAgent) - } - - // add header parameter, if any - if len(headerParams) > 0 { - request.SetHeaders(headerParams) - } - - // add query parameter, if any - if len(queryParams) > 0 { - request.SetMultiValueQueryParams(queryParams) - } - - // add form parameter, if any - if len(formParams) > 0 { - request.SetFormData(formParams) - } - - if len(fileBytes) > 0 && fileName != "" { - _, fileNm := filepath.Split(fileName) - request.SetFileReader("file", fileNm, bytes.NewReader(fileBytes)) - } - return request -} diff --git a/examples/clients/unannotatedecho/configuration.go b/examples/clients/unannotatedecho/configuration.go deleted file mode 100644 index d8ed5f04501..00000000000 --- a/examples/clients/unannotatedecho/configuration.go +++ /dev/null @@ -1,67 +0,0 @@ -/* - * examples/proto/examplepb/unannotated_echo_service.proto - * - * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format. Echo Service API consists of a single service which returns a message. - * - * OpenAPI spec version: version not set - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package unannotatedecho - -import ( - "encoding/base64" - "net/http" - "time" -) - - -type Configuration struct { - Username string `json:"userName,omitempty"` - Password string `json:"password,omitempty"` - APIKeyPrefix map[string]string `json:"APIKeyPrefix,omitempty"` - APIKey map[string]string `json:"APIKey,omitempty"` - Debug bool `json:"debug,omitempty"` - DebugFile string `json:"debugFile,omitempty"` - OAuthToken string `json:"oAuthToken,omitempty"` - BasePath string `json:"basePath,omitempty"` - Host string `json:"host,omitempty"` - Scheme string `json:"scheme,omitempty"` - AccessToken string `json:"accessToken,omitempty"` - DefaultHeader map[string]string `json:"defaultHeader,omitempty"` - UserAgent string `json:"userAgent,omitempty"` - APIClient *APIClient - Transport *http.Transport - Timeout *time.Duration `json:"timeout,omitempty"` -} - -func NewConfiguration() *Configuration { - cfg := &Configuration{ - BasePath: "http://localhost", - DefaultHeader: make(map[string]string), - APIKey: make(map[string]string), - APIKeyPrefix: make(map[string]string), - UserAgent: "Swagger-Codegen/1.0.0/go", - APIClient: &APIClient{}, - } - - cfg.APIClient.config = cfg - return cfg -} - -func (c *Configuration) GetBasicAuthEncodedString() string { - return base64.StdEncoding.EncodeToString([]byte(c.Username + ":" + c.Password)) -} - -func (c *Configuration) AddDefaultHeader(key string, value string) { - c.DefaultHeader[key] = value -} - -func (c *Configuration) GetAPIKeyWithPrefix(APIKeyIdentifier string) string { - if c.APIKeyPrefix[APIKeyIdentifier] != "" { - return c.APIKeyPrefix[APIKeyIdentifier] + " " + c.APIKey[APIKeyIdentifier] - } - - return c.APIKey[APIKeyIdentifier] -} diff --git a/examples/clients/unannotatedecho/examplepb_unannotated_simple_message.go b/examples/clients/unannotatedecho/examplepb_unannotated_simple_message.go deleted file mode 100644 index 7791baa682b..00000000000 --- a/examples/clients/unannotatedecho/examplepb_unannotated_simple_message.go +++ /dev/null @@ -1,22 +0,0 @@ -/* - * examples/proto/examplepb/unannotated_echo_service.proto - * - * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format. Echo Service API consists of a single service which returns a message. - * - * OpenAPI spec version: version not set - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package unannotatedecho - -// UnannotatedSimpleMessage represents a simple message sent to the unannotated Echo service. -type ExamplepbUnannotatedSimpleMessage struct { - - // Id represents the message identifier. - Id string `json:"id,omitempty"` - - Num string `json:"num,omitempty"` - - Duration string `json:"duration,omitempty"` -} diff --git a/examples/clients/unannotatedecho/unannotated_echo_service_api.go b/examples/clients/unannotatedecho/unannotated_echo_service_api.go deleted file mode 100644 index 99f02f08d03..00000000000 --- a/examples/clients/unannotatedecho/unannotated_echo_service_api.go +++ /dev/null @@ -1,290 +0,0 @@ -/* - * examples/proto/examplepb/unannotated_echo_service.proto - * - * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format. Echo Service API consists of a single service which returns a message. - * - * OpenAPI spec version: version not set - * - * Generated by: https://github.com/swagger-api/swagger-codegen.git - */ - -package unannotatedecho - -import ( - "net/url" - "strings" - "encoding/json" - "fmt" -) - -type UnannotatedEchoServiceApi struct { - Configuration *Configuration -} - -func NewUnannotatedEchoServiceApi() *UnannotatedEchoServiceApi { - configuration := NewConfiguration() - return &UnannotatedEchoServiceApi{ - Configuration: configuration, - } -} - -func NewUnannotatedEchoServiceApiWithBasePath(basePath string) *UnannotatedEchoServiceApi { - configuration := NewConfiguration() - configuration.BasePath = basePath - - return &UnannotatedEchoServiceApi{ - Configuration: configuration, - } -} - -/** - * Echo method receives a simple message and returns it. - * The message posted as the id parameter will also be returned. - * - * @param id - * @return *ExamplepbUnannotatedSimpleMessage - */ -func (a UnannotatedEchoServiceApi) Echo(id string) (*ExamplepbUnannotatedSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbUnannotatedSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * Echo method receives a simple message and returns it. - * The message posted as the id parameter will also be returned. - * - * @param id - * @param num - * @param duration - * @return *ExamplepbUnannotatedSimpleMessage - */ -func (a UnannotatedEchoServiceApi) Echo2(id string, num string, duration string) (*ExamplepbUnannotatedSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Get") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo/{id}/{num}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1) - localVarPath = strings.Replace(localVarPath, "{"+"num"+"}", fmt.Sprintf("%v", num), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("duration", a.Configuration.APIClient.ParameterToString(duration, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbUnannotatedSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "Echo2", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * EchoBody method receives a simple message and returns it. - * - * @param body - * @return *ExamplepbUnannotatedSimpleMessage - */ -func (a UnannotatedEchoServiceApi) EchoBody(body ExamplepbUnannotatedSimpleMessage) (*ExamplepbUnannotatedSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Post") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo_body" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - // body params - localVarPostBody = &body - var successPayload = new(ExamplepbUnannotatedSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "EchoBody", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - -/** - * EchoDelete method receives a simple message and returns it. - * - * @param id Id represents the message identifier. - * @param num - * @param duration - * @return *ExamplepbUnannotatedSimpleMessage - */ -func (a UnannotatedEchoServiceApi) EchoDelete(id string, num string, duration string) (*ExamplepbUnannotatedSimpleMessage, *APIResponse, error) { - - var localVarHttpMethod = strings.ToUpper("Delete") - // create path and map variables - localVarPath := a.Configuration.BasePath + "/v1/example/echo_delete" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := make(map[string]string) - var localVarPostBody interface{} - var localVarFileName string - var localVarFileBytes []byte - // add default headers if any - for key := range a.Configuration.DefaultHeader { - localVarHeaderParams[key] = a.Configuration.DefaultHeader[key] - } - localVarQueryParams.Add("id", a.Configuration.APIClient.ParameterToString(id, "")) - localVarQueryParams.Add("num", a.Configuration.APIClient.ParameterToString(num, "")) - localVarQueryParams.Add("duration", a.Configuration.APIClient.ParameterToString(duration, "")) - - // to determine the Content-Type header - localVarHttpContentTypes := []string{ "application/json", } - - // set Content-Type header - localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes) - if localVarHttpContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHttpContentType - } - // to determine the Accept header - localVarHttpHeaderAccepts := []string{ - "application/json", - } - - // set Accept header - localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts) - if localVarHttpHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHttpHeaderAccept - } - var successPayload = new(ExamplepbUnannotatedSimpleMessage) - localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes) - - var localVarURL, _ = url.Parse(localVarPath) - localVarURL.RawQuery = localVarQueryParams.Encode() - var localVarAPIResponse = &APIResponse{Operation: "EchoDelete", Method: localVarHttpMethod, RequestURL: localVarURL.String()} - if localVarHttpResponse != nil { - localVarAPIResponse.Response = localVarHttpResponse.RawResponse - localVarAPIResponse.Payload = localVarHttpResponse.Body() - } - - if err != nil { - return successPayload, localVarAPIResponse, err - } - err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload) - return successPayload, localVarAPIResponse, err -} - diff --git a/examples/cmd/example-gateway-server/BUILD.bazel b/examples/cmd/example-gateway-server/BUILD.bazel deleted file mode 100644 index 0f7c5052e7e..00000000000 --- a/examples/cmd/example-gateway-server/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") - -go_library( - name = "go_default_library", - srcs = ["main.go"], - importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/cmd/example-gateway-server", - visibility = ["//visibility:private"], - deps = [ - "//examples/gateway:go_default_library", - "@com_github_golang_glog//:go_default_library", - ], -) - -go_binary( - name = "example-gateway-server", - embed = [":go_default_library"], - visibility = ["//visibility:public"], -) diff --git a/examples/gateway/BUILD.bazel b/examples/gateway/BUILD.bazel deleted file mode 100644 index 393a7c41f9c..00000000000 --- a/examples/gateway/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = [ - "doc.go", - "gateway.go", - "handlers.go", - "main.go", - ], - importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/gateway", - visibility = ["//visibility:public"], - deps = [ - "//examples/proto/examplepb:go_default_library", - "//runtime:go_default_library", - "@com_github_golang_glog//:go_default_library", - "@org_golang_google_grpc//:go_default_library", - "@org_golang_google_grpc//connectivity:go_default_library", - ], -) diff --git a/examples/gateway/doc.go b/examples/gateway/doc.go deleted file mode 100644 index a9ca836960e..00000000000 --- a/examples/gateway/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package gateway is an example of grpc-gateway server -package gateway diff --git a/examples/integration/BUILD.bazel b/examples/integration/BUILD.bazel deleted file mode 100644 index c603d01e14f..00000000000 --- a/examples/integration/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "go_default_xtest", - srcs = [ - "client_test.go", - "integration_test.go", - "main_test.go", - "proto_error_test.go", - ], - deps = [ - "//examples/clients/abe:go_default_library", - "//examples/clients/echo:go_default_library", - "//examples/clients/unannotatedecho:go_default_library", - "//examples/gateway:go_default_library", - "//examples/proto/examplepb:go_default_library", - "//examples/proto/sub:go_default_library", - "//examples/server:go_default_library", - "//runtime:go_default_library", - "@com_github_golang_glog//:go_default_library", - "@com_github_golang_protobuf//jsonpb:go_default_library", - "@com_github_golang_protobuf//proto:go_default_library", - "@com_github_golang_protobuf//ptypes/empty:go_default_library", - "@org_golang_google_genproto//googleapis/rpc/status:go_default_library", - "@org_golang_google_grpc//codes:go_default_library", - ], -) diff --git a/examples/integration/client_test.go b/examples/integration/client_test.go deleted file mode 100644 index b49a2f31fff..00000000000 --- a/examples/integration/client_test.go +++ /dev/null @@ -1,196 +0,0 @@ -package integration_test - -import ( - "reflect" - "testing" - - "github.com/grpc-ecosystem/grpc-gateway/examples/clients/abe" - "github.com/grpc-ecosystem/grpc-gateway/examples/clients/echo" - "github.com/grpc-ecosystem/grpc-gateway/examples/clients/unannotatedecho" -) - -func TestClientIntegration(t *testing.T) { -} - -func TestEchoClient(t *testing.T) { - if testing.Short() { - t.Skip() - return - } - - cl := echo.NewEchoServiceApiWithBasePath("http://localhost:8080") - resp, _, err := cl.Echo("foo") - if err != nil { - t.Errorf(`cl.Echo("foo") failed with %v; want success`, err) - } - if got, want := resp.Id, "foo"; got != want { - t.Errorf("resp.Id = %q; want %q", got, want) - } -} - -func TestEchoBodyClient(t *testing.T) { - if testing.Short() { - t.Skip() - return - } - - cl := echo.NewEchoServiceApiWithBasePath("http://localhost:8080") - req := echo.ExamplepbSimpleMessage{Id: "foo"} - resp, _, err := cl.EchoBody(req) - if err != nil { - t.Errorf("cl.EchoBody(%#v) failed with %v; want success", req, err) - } - if got, want := resp.Id, "foo"; got != want { - t.Errorf("resp.Id = %q; want %q", got, want) - } -} - -func TestAbitOfEverythingClient(t *testing.T) { - if testing.Short() { - t.Skip() - return - } - - cl := abe.NewABitOfEverythingServiceApiWithBasePath("http://localhost:8080") - testABEClientCreate(t, cl) - testABEClientCreateBody(t, cl) -} - -func testABEClientCreate(t *testing.T, cl *abe.ABitOfEverythingServiceApi) { - want := &abe.ExamplepbABitOfEverything{ - FloatValue: 1.5, - DoubleValue: 2.5, - Int64Value: "4294967296", - Uint64Value: "9223372036854775807", - Int32Value: -2147483648, - Fixed64Value: "9223372036854775807", - Fixed32Value: 4294967295, - BoolValue: true, - StringValue: "strprefix/foo", - Uint32Value: 4294967295, - Sfixed32Value: 2147483647, - Sfixed64Value: "-4611686018427387904", - Sint32Value: 2147483647, - Sint64Value: "4611686018427387903", - NonConventionalNameValue: "camelCase", - } - resp, _, err := cl.Create( - want.FloatValue, - want.DoubleValue, - want.Int64Value, - want.Uint64Value, - want.Int32Value, - want.Fixed64Value, - want.Fixed32Value, - want.BoolValue, - want.StringValue, - want.Uint32Value, - want.Sfixed32Value, - want.Sfixed64Value, - want.Sint32Value, - want.Sint64Value, - want.NonConventionalNameValue, - ) - if err != nil { - t.Errorf("cl.Create(%#v) failed with %v; want success", want, err) - } - if resp.Uuid == "" { - t.Errorf("resp.Uuid is empty; want not empty") - } - resp.Uuid = "" - if got := resp; !reflect.DeepEqual(got, want) { - t.Errorf("resp = %#v; want %#v", got, want) - } -} - -func testABEClientCreateBody(t *testing.T, cl *abe.ABitOfEverythingServiceApi) { - t.Log("TODO: support enum") - return - - want := abe.ExamplepbABitOfEverything{ - FloatValue: 1.5, - DoubleValue: 2.5, - Int64Value: "4294967296", - Uint64Value: "9223372036854775807", - Int32Value: -2147483648, - Fixed64Value: "9223372036854775807", - Fixed32Value: 4294967295, - BoolValue: true, - StringValue: "strprefix/foo", - Uint32Value: 4294967295, - Sfixed32Value: 2147483647, - Sfixed64Value: "-4611686018427387904", - Sint32Value: 2147483647, - Sint64Value: "4611686018427387903", - NonConventionalNameValue: "camelCase", - - Nested: []abe.ABitOfEverythingNested{ - { - Name: "bar", - Amount: 10, - }, - { - Name: "baz", - Amount: 20, - }, - }, - RepeatedStringValue: []string{"a", "b", "c"}, - OneofString: "x", - MapValue: map[string]abe.ExamplepbNumericEnum{ - // "a": abe.ExamplepbNumericEnum_ONE, - // "b": abe.ExamplepbNumericEnum_ZERO, - }, - MappedStringValue: map[string]string{ - "a": "x", - "b": "y", - }, - MappedNestedValue: map[string]abe.ABitOfEverythingNested{ - "a": {Name: "x", Amount: 1}, - "b": {Name: "y", Amount: 2}, - }, - } - resp, _, err := cl.CreateBody(want) - if err != nil { - t.Errorf("cl.CreateBody(%#v) failed with %v; want success", want, err) - } - if resp.Uuid == "" { - t.Errorf("resp.Uuid is empty; want not empty") - } - resp.Uuid = "" - if got := resp; !reflect.DeepEqual(got, want) { - t.Errorf("resp = %#v; want %#v", got, want) - } -} - -func TestUnannotatedEchoClient(t *testing.T) { - if testing.Short() { - t.Skip() - return - } - - cl := unannotatedecho.NewUnannotatedEchoServiceApiWithBasePath("http://localhost:8080") - resp, _, err := cl.Echo("foo") - if err != nil { - t.Errorf(`cl.Echo("foo") failed with %v; want success`, err) - } - if got, want := resp.Id, "foo"; got != want { - t.Errorf("resp.Id = %q; want %q", got, want) - } -} - -func TestUnannotatedEchoBodyClient(t *testing.T) { - if testing.Short() { - t.Skip() - return - } - - cl := unannotatedecho.NewUnannotatedEchoServiceApiWithBasePath("http://localhost:8080") - req := unannotatedecho.ExamplepbUnannotatedSimpleMessage{Id: "foo"} - resp, _, err := cl.EchoBody(req) - if err != nil { - t.Errorf("cl.EchoBody(%#v) failed with %v; want success", req, err) - } - if got, want := resp.Id, "foo"; got != want { - t.Errorf("resp.Id = %q; want %q", got, want) - } -} diff --git a/examples/integration/integration_test.go b/examples/integration/integration_test.go deleted file mode 100644 index 0c2885c4557..00000000000 --- a/examples/integration/integration_test.go +++ /dev/null @@ -1,982 +0,0 @@ -package integration_test - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "net/http" - "reflect" - "strconv" - "strings" - "sync" - "testing" - - "github.com/golang/protobuf/jsonpb" - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes/empty" - gw "github.com/grpc-ecosystem/grpc-gateway/examples/proto/examplepb" - sub "github.com/grpc-ecosystem/grpc-gateway/examples/proto/sub" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "google.golang.org/grpc/codes" -) - -type errorBody struct { - Error string `json:"error"` - Code int `json:"code"` - Details []interface{} `json:"details"` -} - -func TestEcho(t *testing.T) { - if testing.Short() { - t.Skip() - return - } - - testEcho(t, 8080, "application/json") - testEchoOneof(t, 8080, "application/json") - testEchoOneof1(t, 8080, "application/json") - testEchoOneof2(t, 8080, "application/json") - testEchoBody(t, 8080) -} - -func TestForwardResponseOption(t *testing.T) { - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - go func() { - if err := runGateway( - ctx, - ":8081", - runtime.WithForwardResponseOption( - func(_ context.Context, w http.ResponseWriter, _ proto.Message) error { - w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1.1+json") - return nil - }, - ), - ); err != nil { - t.Errorf("runGateway() failed with %v; want success", err) - return - } - }() - if err := waitForGateway(ctx, 8081); err != nil { - t.Errorf("waitForGateway(ctx, 8081) failed with %v; want success", err) - } - testEcho(t, 8081, "application/vnd.docker.plugins.v1.1+json") -} - -func testEcho(t *testing.T, port int, contentType string) { - url := fmt.Sprintf("http://localhost:%d/v1/example/echo/myid", port) - resp, err := http.Post(url, "application/json", strings.NewReader("{}")) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg gw.SimpleMessage - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - if got, want := msg.Id, "myid"; got != want { - t.Errorf("msg.Id = %q; want %q", got, want) - } - - if value := resp.Header.Get("Content-Type"); value != contentType { - t.Errorf("Content-Type was %s, wanted %s", value, contentType) - } -} - -func testEchoOneof(t *testing.T, port int, contentType string) { - url := fmt.Sprintf("http://localhost:%d/v1/example/echo/myid/10/golang", port) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg gw.SimpleMessage - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - if got, want := msg.GetLang(), "golang"; got != want { - t.Errorf("msg.GetLang() = %q; want %q", got, want) - } - - if value := resp.Header.Get("Content-Type"); value != contentType { - t.Errorf("Content-Type was %s, wanted %s", value, contentType) - } -} - -func testEchoOneof1(t *testing.T, port int, contentType string) { - url := fmt.Sprintf("http://localhost:%d/v1/example/echo1/myid/10/golang", port) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg gw.SimpleMessage - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - if got, want := msg.GetStatus().GetNote(), "golang"; got != want { - t.Errorf("msg.GetStatus().GetNote() = %q; want %q", got, want) - } - - if value := resp.Header.Get("Content-Type"); value != contentType { - t.Errorf("Content-Type was %s, wanted %s", value, contentType) - } -} - -func testEchoOneof2(t *testing.T, port int, contentType string) { - url := fmt.Sprintf("http://localhost:%d/v1/example/echo2/golang", port) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg gw.SimpleMessage - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - if got, want := msg.GetNo().GetNote(), "golang"; got != want { - t.Errorf("msg.GetNo().GetNote() = %q; want %q", got, want) - } - - if value := resp.Header.Get("Content-Type"); value != contentType { - t.Errorf("Content-Type was %s, wanted %s", value, contentType) - } -} - -func testEchoBody(t *testing.T, port int) { - sent := gw.SimpleMessage{Id: "example"} - var m jsonpb.Marshaler - payload, err := m.MarshalToString(&sent) - if err != nil { - t.Fatalf("m.MarshalToString(%#v) failed with %v; want success", payload, err) - } - - url := fmt.Sprintf("http://localhost:%d/v1/example/echo_body", port) - resp, err := http.Post(url, "", strings.NewReader(payload)) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var received gw.SimpleMessage - if err := jsonpb.UnmarshalString(string(buf), &received); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - if got, want := received, sent; !reflect.DeepEqual(got, want) { - t.Errorf("msg.Id = %q; want %q", got, want) - } - - if got, want := resp.Header.Get("Grpc-Metadata-Foo"), "foo1"; got != want { - t.Errorf("Grpc-Metadata-Foo was %q, wanted %q", got, want) - } - if got, want := resp.Header.Get("Grpc-Metadata-Bar"), "bar1"; got != want { - t.Errorf("Grpc-Metadata-Bar was %q, wanted %q", got, want) - } - - if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want { - t.Errorf("Grpc-Trailer-Foo was %q, wanted %q", got, want) - } - if got, want := resp.Trailer.Get("Grpc-Trailer-Bar"), "bar2"; got != want { - t.Errorf("Grpc-Trailer-Bar was %q, wanted %q", got, want) - } -} - -func TestABE(t *testing.T) { - if testing.Short() { - t.Skip() - return - } - - testABECreate(t, 8080) - testABECreateBody(t, 8080) - testABEBulkCreate(t, 8080) - testABELookup(t, 8080) - testABELookupNotFound(t, 8080) - testABEList(t, 8080) - testABEBulkEcho(t, 8080) - testABEBulkEchoZeroLength(t, 8080) - testAdditionalBindings(t, 8080) -} - -func testABECreate(t *testing.T, port int) { - want := gw.ABitOfEverything{ - FloatValue: 1.5, - DoubleValue: 2.5, - Int64Value: 4294967296, - Uint64Value: 9223372036854775807, - Int32Value: -2147483648, - Fixed64Value: 9223372036854775807, - Fixed32Value: 4294967295, - BoolValue: true, - StringValue: "strprefix/foo", - Uint32Value: 4294967295, - Sfixed32Value: 2147483647, - Sfixed64Value: -4611686018427387904, - Sint32Value: 2147483647, - Sint64Value: 4611686018427387903, - NonConventionalNameValue: "camelCase", - } - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything/%f/%f/%d/separator/%d/%d/%d/%d/%v/%s/%d/%d/%d/%d/%d/%s", port, want.FloatValue, want.DoubleValue, want.Int64Value, want.Uint64Value, want.Int32Value, want.Fixed64Value, want.Fixed32Value, want.BoolValue, want.StringValue, want.Uint32Value, want.Sfixed32Value, want.Sfixed64Value, want.Sint32Value, want.Sint64Value, want.NonConventionalNameValue) - - resp, err := http.Post(url, "application/json", strings.NewReader("{}")) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg gw.ABitOfEverything - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - if msg.Uuid == "" { - t.Error("msg.Uuid is empty; want not empty") - } - msg.Uuid = "" - if got := msg; !reflect.DeepEqual(got, want) { - t.Errorf("msg= %v; want %v", &got, &want) - } -} - -func testABECreateBody(t *testing.T, port int) { - want := gw.ABitOfEverything{ - FloatValue: 1.5, - DoubleValue: 2.5, - Int64Value: 4294967296, - Uint64Value: 9223372036854775807, - Int32Value: -2147483648, - Fixed64Value: 9223372036854775807, - Fixed32Value: 4294967295, - BoolValue: true, - StringValue: "strprefix/foo", - Uint32Value: 4294967295, - Sfixed32Value: 2147483647, - Sfixed64Value: -4611686018427387904, - Sint32Value: 2147483647, - Sint64Value: 4611686018427387903, - NonConventionalNameValue: "camelCase", - - Nested: []*gw.ABitOfEverything_Nested{ - { - Name: "bar", - Amount: 10, - }, - { - Name: "baz", - Amount: 20, - }, - }, - RepeatedStringValue: []string{"a", "b", "c"}, - OneofValue: &gw.ABitOfEverything_OneofString{ - OneofString: "x", - }, - MapValue: map[string]gw.NumericEnum{ - "a": gw.NumericEnum_ONE, - "b": gw.NumericEnum_ZERO, - }, - MappedStringValue: map[string]string{ - "a": "x", - "b": "y", - }, - MappedNestedValue: map[string]*gw.ABitOfEverything_Nested{ - "a": {Name: "x", Amount: 1}, - "b": {Name: "y", Amount: 2}, - }, - } - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything", port) - var m jsonpb.Marshaler - payload, err := m.MarshalToString(&want) - if err != nil { - t.Fatalf("m.MarshalToString(%#v) failed with %v; want success", want, err) - } - - resp, err := http.Post(url, "application/json", strings.NewReader(payload)) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg gw.ABitOfEverything - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - if msg.Uuid == "" { - t.Error("msg.Uuid is empty; want not empty") - } - msg.Uuid = "" - if got := msg; !reflect.DeepEqual(got, want) { - t.Errorf("msg= %v; want %v", &got, &want) - } -} - -func testABEBulkCreate(t *testing.T, port int) { - count := 0 - r, w := io.Pipe() - go func(w io.WriteCloser) { - defer func() { - if cerr := w.Close(); cerr != nil { - t.Errorf("w.Close() failed with %v; want success", cerr) - } - }() - for _, val := range []string{ - "foo", "bar", "baz", "qux", "quux", - } { - want := gw.ABitOfEverything{ - FloatValue: 1.5, - DoubleValue: 2.5, - Int64Value: 4294967296, - Uint64Value: 9223372036854775807, - Int32Value: -2147483648, - Fixed64Value: 9223372036854775807, - Fixed32Value: 4294967295, - BoolValue: true, - StringValue: fmt.Sprintf("strprefix/%s", val), - Uint32Value: 4294967295, - Sfixed32Value: 2147483647, - Sfixed64Value: -4611686018427387904, - Sint32Value: 2147483647, - Sint64Value: 4611686018427387903, - NonConventionalNameValue: "camelCase", - - Nested: []*gw.ABitOfEverything_Nested{ - { - Name: "hoge", - Amount: 10, - }, - { - Name: "fuga", - Amount: 20, - }, - }, - } - var m jsonpb.Marshaler - if err := m.Marshal(w, &want); err != nil { - t.Fatalf("m.Marshal(%#v, w) failed with %v; want success", want, err) - } - if _, err := io.WriteString(w, "\n"); err != nil { - t.Errorf("w.Write(%q) failed with %v; want success", "\n", err) - return - } - count++ - } - }(w) - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything/bulk", port) - resp, err := http.Post(url, "application/json", r) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg empty.Empty - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - - if got, want := resp.Header.Get("Grpc-Metadata-Count"), fmt.Sprintf("%d", count); got != want { - t.Errorf("Grpc-Metadata-Count was %q, wanted %q", got, want) - } - - if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want { - t.Errorf("Grpc-Trailer-Foo was %q, wanted %q", got, want) - } - if got, want := resp.Trailer.Get("Grpc-Trailer-Bar"), "bar2"; got != want { - t.Errorf("Grpc-Trailer-Bar was %q, wanted %q", got, want) - } -} - -func testABELookup(t *testing.T, port int) { - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything", port) - cresp, err := http.Post(url, "application/json", strings.NewReader(` - {"bool_value": true, "string_value": "strprefix/example"} - `)) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer cresp.Body.Close() - buf, err := ioutil.ReadAll(cresp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(cresp.Body) failed with %v; want success", err) - return - } - if got, want := cresp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - return - } - - var want gw.ABitOfEverything - if err := jsonpb.UnmarshalString(string(buf), &want); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &want) failed with %v; want success", buf, err) - return - } - - url = fmt.Sprintf("%s/%s", url, want.Uuid) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - - buf, err = ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("ioutil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - var msg gw.ABitOfEverything - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - if got := msg; !reflect.DeepEqual(got, want) { - t.Errorf("msg= %v; want %v", &got, &want) - } - - if got, want := resp.Header.Get("Grpc-Metadata-Uuid"), want.Uuid; got != want { - t.Errorf("Grpc-Metadata-Uuid was %s, wanted %s", got, want) - } -} - -func testABELookupNotFound(t *testing.T, port int) { - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything", port) - uuid := "not_exist" - url = fmt.Sprintf("%s/%s", url, uuid) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("ioutil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusNotFound; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - return - } - - var msg errorBody - if err := json.Unmarshal(buf, &msg); err != nil { - t.Errorf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err) - return - } - - if got, want := msg.Code, int(codes.NotFound); got != want { - t.Errorf("msg.Code = %d; want %d", got, want) - return - } - - if got, want := msg.Error, "not found"; got != want { - t.Errorf("msg.Error = %s; want %s", got, want) - return - } - - if got, want := resp.Header.Get("Grpc-Metadata-Uuid"), uuid; got != want { - t.Errorf("Grpc-Metadata-Uuid was %s, wanted %s", got, want) - } - if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want { - t.Errorf("Grpc-Trailer-Foo was %q, wanted %q", got, want) - } - if got, want := resp.Trailer.Get("Grpc-Trailer-Bar"), "bar2"; got != want { - t.Errorf("Grpc-Trailer-Bar was %q, wanted %q", got, want) - } -} - -func testABEList(t *testing.T, port int) { - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything", port) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - - dec := json.NewDecoder(resp.Body) - var i int - for i = 0; ; i++ { - var item struct { - Result json.RawMessage `json:"result"` - Error map[string]interface{} `json:"error"` - } - err := dec.Decode(&item) - if err == io.EOF { - break - } - if err != nil { - t.Errorf("dec.Decode(&item) failed with %v; want success; i = %d", err, i) - } - if len(item.Error) != 0 { - t.Errorf("item.Error = %#v; want empty; i = %d", item.Error, i) - continue - } - var msg gw.ABitOfEverything - if err := jsonpb.UnmarshalString(string(item.Result), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", item.Result, err) - } - } - if i <= 0 { - t.Errorf("i == %d; want > 0", i) - } - - value := resp.Header.Get("Grpc-Metadata-Count") - if value == "" { - t.Errorf("Grpc-Metadata-Count should not be empty") - } - - count, err := strconv.Atoi(value) - if err != nil { - t.Errorf("failed to Atoi %q: %v", value, err) - } - - if count <= 0 { - t.Errorf("count == %d; want > 0", count) - } -} - -func testABEBulkEcho(t *testing.T, port int) { - reqr, reqw := io.Pipe() - var wg sync.WaitGroup - var want []*sub.StringMessage - wg.Add(1) - go func() { - defer wg.Done() - defer reqw.Close() - var m jsonpb.Marshaler - for i := 0; i < 1000; i++ { - msg := sub.StringMessage{Value: proto.String(fmt.Sprintf("message %d", i))} - buf, err := m.MarshalToString(&msg) - if err != nil { - t.Errorf("m.Marshal(%v) failed with %v; want success", &msg, err) - return - } - if _, err := fmt.Fprintln(reqw, buf); err != nil { - t.Errorf("fmt.Fprintln(reqw, %q) failed with %v; want success", buf, err) - return - } - want = append(want, &msg) - } - }() - - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything/echo", port) - req, err := http.NewRequest("POST", url, reqr) - if err != nil { - t.Errorf("http.NewRequest(%q, %q, reqr) failed with %v; want success", "POST", url, err) - return - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Transfer-Encoding", "chunked") - resp, err := http.DefaultClient.Do(req) - if err != nil { - t.Errorf("http.Post(%q, %q, req) failed with %v; want success", url, "application/json", err) - return - } - defer resp.Body.Close() - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - } - - var got []*sub.StringMessage - wg.Add(1) - go func() { - defer wg.Done() - - dec := json.NewDecoder(resp.Body) - for i := 0; ; i++ { - var item struct { - Result json.RawMessage `json:"result"` - Error map[string]interface{} `json:"error"` - } - err := dec.Decode(&item) - if err == io.EOF { - break - } - if err != nil { - t.Errorf("dec.Decode(&item) failed with %v; want success; i = %d", err, i) - } - if len(item.Error) != 0 { - t.Errorf("item.Error = %#v; want empty; i = %d", item.Error, i) - continue - } - var msg sub.StringMessage - if err := jsonpb.UnmarshalString(string(item.Result), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%q, &msg) failed with %v; want success", item.Result, err) - } - got = append(got, &msg) - } - }() - - wg.Wait() - if !reflect.DeepEqual(got, want) { - t.Errorf("got = %v; want %v", got, want) - } -} - -func testABEBulkEchoZeroLength(t *testing.T, port int) { - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything/echo", port) - req, err := http.NewRequest("POST", url, bytes.NewReader(nil)) - if err != nil { - t.Errorf("http.NewRequest(%q, %q, bytes.NewReader(nil)) failed with %v; want success", "POST", url, err) - return - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Transfer-Encoding", "chunked") - resp, err := http.DefaultClient.Do(req) - if err != nil { - t.Errorf("http.Post(%q, %q, req) failed with %v; want success", url, "application/json", err) - return - } - defer resp.Body.Close() - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - } - - dec := json.NewDecoder(resp.Body) - var item struct { - Result json.RawMessage `json:"result"` - Error map[string]interface{} `json:"error"` - } - if err := dec.Decode(&item); err == nil { - t.Errorf("dec.Decode(&item) succeeded; want io.EOF; item = %#v", item) - } else if err != io.EOF { - t.Errorf("dec.Decode(&item) failed with %v; want success", err) - return - } -} - -func testAdditionalBindings(t *testing.T, port int) { - for i, f := range []func() *http.Response{ - func() *http.Response { - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything/echo/hello", port) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return nil - } - return resp - }, - func() *http.Response { - url := fmt.Sprintf("http://localhost:%d/v2/example/echo", port) - resp, err := http.Post(url, "application/json", strings.NewReader(`"hello"`)) - if err != nil { - t.Errorf("http.Post(%q, %q, %q) failed with %v; want success", url, "application/json", `"hello"`, err) - return nil - } - return resp - }, - func() *http.Response { - r, w := io.Pipe() - go func() { - defer w.Close() - w.Write([]byte(`"hello"`)) - }() - url := fmt.Sprintf("http://localhost:%d/v2/example/echo", port) - resp, err := http.Post(url, "application/json", r) - if err != nil { - t.Errorf("http.Post(%q, %q, %q) failed with %v; want success", url, "application/json", `"hello"`, err) - return nil - } - return resp - }, - func() *http.Response { - url := fmt.Sprintf("http://localhost:%d/v2/example/echo?value=hello", port) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return nil - } - return resp - }, - } { - resp := f() - if resp == nil { - continue - } - - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success; i=%d", err, i) - return - } - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d; i=%d", got, want, i) - t.Logf("%s", buf) - } - - var msg sub.StringMessage - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success; %d", buf, err, i) - return - } - if got, want := msg.GetValue(), "hello"; got != want { - t.Errorf("msg.GetValue() = %q; want %q", got, want) - } - } -} - -func TestTimeout(t *testing.T) { - url := "http://localhost:8080/v2/example/timeout" - req, err := http.NewRequest("GET", url, nil) - if err != nil { - t.Errorf(`http.NewRequest("GET", %q, nil) failed with %v; want success`, url, err) - return - } - req.Header.Set("Grpc-Timeout", "10m") - resp, err := http.DefaultClient.Do(req) - if err != nil { - t.Errorf("http.DefaultClient.Do(%#v) failed with %v; want success", req, err) - return - } - defer resp.Body.Close() - - if got, want := resp.StatusCode, http.StatusGatewayTimeout; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - } -} - -func TestErrorWithDetails(t *testing.T) { - url := "http://localhost:8080/v2/example/errorwithdetails" - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - } - - if got, want := resp.StatusCode, http.StatusInternalServerError; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - } - - var msg errorBody - if err := json.Unmarshal(buf, &msg); err != nil { - t.Fatalf("json.Unmarshal(%s, &msg) failed with %v; want success", buf, err) - } - - if got, want := msg.Code, int(codes.Unknown); got != want { - t.Errorf("msg.Code = %d; want %d", got, want) - } - if got, want := msg.Error, "with details"; got != want { - t.Errorf("msg.Error = %s; want %s", got, want) - } - if got, want := len(msg.Details), 1; got != want { - t.Fatalf("len(msg.Details) = %q; want %q", got, want) - } - - details, ok := msg.Details[0].(map[string]interface{}) - if got, want := ok, true; got != want { - t.Fatalf("msg.Details[0] got type: %T, want %T", msg.Details[0], map[string]interface{}{}) - } - typ, ok := details["@type"].(string) - if got, want := ok, true; got != want { - t.Fatalf("msg.Details[0][\"@type\"] got type: %T, want %T", typ, "") - } - if got, want := details["@type"], "type.googleapis.com/google.rpc.DebugInfo"; got != want { - t.Errorf("msg.Details[\"@type\"] = %q; want %q", got, want) - } - if got, want := details["detail"], "error debug details"; got != want { - t.Errorf("msg.Details[\"detail\"] = %q; want %q", got, want) - } - entries, ok := details["stack_entries"].([]interface{}) - if got, want := ok, true; got != want { - t.Fatalf("msg.Details[0][\"stack_entries\"] got type: %T, want %T", entries, []string{}) - } - entry, ok := entries[0].(string) - if got, want := ok, true; got != want { - t.Fatalf("msg.Details[0][\"stack_entries\"][0] got type: %T, want %T", entry, "") - } - if got, want := entries[0], "foo:1"; got != want { - t.Errorf("msg.Details[\"stack_entries\"][0] = %q; want %q", got, want) - } -} - -func TestPostWithEmptyBody(t *testing.T) { - url := "http://localhost:8080/v2/example/postwithemptybody/name" - rep, err := http.Post(url, "application/json", nil) - - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - - if rep.StatusCode != http.StatusOK { - t.Errorf("http.Post(%q) response code is %d; want %d", url, - rep.StatusCode, http.StatusOK) - return - } -} - -func TestUnknownPath(t *testing.T) { - url := "http://localhost:8080" - resp, err := http.Post(url, "application/json", strings.NewReader("{}")) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusNotFound; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } -} - -func TestMethodNotAllowed(t *testing.T) { - url := "http://localhost:8080/v1/example/echo/myid" - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusMethodNotAllowed; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } -} - -func TestInvalidArgument(t *testing.T) { - url := "http://localhost:8080/v1/example/echo/myid/not_int64" - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusBadRequest; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } -} diff --git a/examples/integration/proto_error_test.go b/examples/integration/proto_error_test.go deleted file mode 100644 index 84a631f9b1c..00000000000 --- a/examples/integration/proto_error_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package integration_test - -import ( - "context" - "fmt" - "io/ioutil" - "net/http" - "strings" - "testing" - "time" - - "github.com/golang/protobuf/jsonpb" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - spb "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc/codes" -) - -func runServer(ctx context.Context, t *testing.T, port uint16) { - opt := runtime.WithProtoErrorHandler(runtime.DefaultHTTPProtoErrorHandler) - if err := runGateway(ctx, fmt.Sprintf(":%d", port), opt); err != nil { - t.Errorf("runGateway() failed with %v; want success", err) - } -} - -func TestWithProtoErrorHandler(t *testing.T) { - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - const port = 8082 - go runServer(ctx, t, port) - if err := waitForGateway(ctx, 8082); err != nil { - t.Errorf("waitForGateway(ctx, 8082) failed with %v; want success", err) - } - testEcho(t, port, "application/json") - testEchoBody(t, port) -} - -func TestABEWithProtoErrorHandler(t *testing.T) { - if testing.Short() { - t.Skip() - return - } - - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - const port = 8083 - go runServer(ctx, t, port) - if err := waitForGateway(ctx, 8083); err != nil { - t.Errorf("waitForGateway(ctx, 8083) failed with %v; want success", err) - } - - testABECreate(t, port) - testABECreateBody(t, port) - testABEBulkCreate(t, port) - testABELookup(t, port) - testABELookupNotFoundWithProtoError(t, port) - testABEList(t, port) - testABEBulkEcho(t, port) - testABEBulkEchoZeroLength(t, port) - testAdditionalBindings(t, port) -} - -func testABELookupNotFoundWithProtoError(t *testing.T, port uint16) { - url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything", port) - uuid := "not_exist" - url = fmt.Sprintf("%s/%s", url, uuid) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Get(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("ioutil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusNotFound; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - return - } - - var msg spb.Status - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - - if got, want := msg.Code, int32(codes.NotFound); got != want { - t.Errorf("msg.Code = %d; want %d", got, want) - return - } - - if got, want := msg.Message, "not found"; got != want { - t.Errorf("msg.Message = %s; want %s", got, want) - return - } - - if got, want := resp.Header.Get("Grpc-Metadata-Uuid"), uuid; got != want { - t.Errorf("Grpc-Metadata-Uuid was %s, wanted %s", got, want) - } - if got, want := resp.Trailer.Get("Grpc-Trailer-Foo"), "foo2"; got != want { - t.Errorf("Grpc-Trailer-Foo was %q, wanted %q", got, want) - } - if got, want := resp.Trailer.Get("Grpc-Trailer-Bar"), "bar2"; got != want { - t.Errorf("Grpc-Trailer-Bar was %q, wanted %q", got, want) - } -} - -func TestUnknownPathWithProtoError(t *testing.T) { - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - const port = 8084 - go runServer(ctx, t, port) - if err := waitForGateway(ctx, 8084); err != nil { - t.Errorf("waitForGateway(ctx, 8084) failed with %v; want success", err) - } - - url := fmt.Sprintf("http://localhost:%d", port) - resp, err := http.Post(url, "application/json", strings.NewReader("{}")) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusNotImplemented; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg spb.Status - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - - if got, want := msg.Code, int32(codes.Unimplemented); got != want { - t.Errorf("msg.Code = %d; want %d", got, want) - return - } - - if msg.Message == "" { - t.Errorf("msg.Message should not be empty") - return - } -} - -func TestMethodNotAllowedWithProtoError(t *testing.T) { - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - const port = 8085 - go runServer(ctx, t, port) - - // Waiting for the server's getting available. - // TODO(yugui) find a better way to wait - time.Sleep(100 * time.Millisecond) - - url := fmt.Sprintf("http://localhost:%d/v1/example/echo/myid", port) - resp, err := http.Get(url) - if err != nil { - t.Errorf("http.Post(%q) failed with %v; want success", url, err) - return - } - defer resp.Body.Close() - buf, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err) - return - } - - if got, want := resp.StatusCode, http.StatusNotImplemented; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } - - var msg spb.Status - if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil { - t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err) - return - } - - if got, want := msg.Code, int32(codes.Unimplemented); got != want { - t.Errorf("msg.Code = %d; want %d", got, want) - return - } - - if msg.Message == "" { - t.Errorf("msg.Message should not be empty") - return - } -} diff --git a/examples/README.md b/examples/internal/README.md similarity index 80% rename from examples/README.md rename to examples/internal/README.md index b7b486f244f..55a1234d694 100644 --- a/examples/README.md +++ b/examples/internal/README.md @@ -8,8 +8,8 @@ $ dep init Follow the guides from this [README.md](./browser/README.md) to run the server and gateway. ```bash # Make sure you are in the correct directory: -# $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/examples -$ cd examples/browser +# $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/v2/examples +$ cd examples/internal/browser $ pwd # Install gulp @@ -26,7 +26,7 @@ Then you can use curl or a browser to test: ```bash # List all apis -$ curl http://localhost:8080/swagger/echo_service.swagger.json +$ curl http://localhost:8080/openapiv2/echo_service.swagger.json # Visit the apis $ curl -XPOST http://localhost:8080/v1/example/echo/foo diff --git a/examples/browser/.gitignore b/examples/internal/browser/.gitignore similarity index 62% rename from examples/browser/.gitignore rename to examples/internal/browser/.gitignore index 1dcc590b085..75ce18e9269 100644 --- a/examples/browser/.gitignore +++ b/examples/internal/browser/.gitignore @@ -1,3 +1,2 @@ /bower_components /node_modules -/package-lock.json diff --git a/examples/browser/README.md b/examples/internal/browser/README.md similarity index 86% rename from examples/browser/README.md rename to examples/internal/browser/README.md index be4fbd31141..24f0fd1eb59 100644 --- a/examples/browser/README.md +++ b/examples/internal/browser/README.md @@ -1,6 +1,6 @@ # Browser example -This directory contains an example use of grpc-gateway with web browsers. +This directory contains an example use of gRPC-Gateway with web browsers. The following commands automatically runs integration tests with phantomjs. ```shell-session @@ -12,7 +12,9 @@ $ gulp ## Other examples ### Very simple example + Run + ```shell-session $ gulp bower $ gulp backends @@ -20,10 +22,10 @@ $ gulp backends then, open `index.html`. - ### Integration test with your browser Run + ```shell-session $ gulp serve ``` diff --git a/examples/internal/browser/a_bit_of_everything_service.spec.js b/examples/internal/browser/a_bit_of_everything_service.spec.js new file mode 100644 index 00000000000..eb6058d2818 --- /dev/null +++ b/examples/internal/browser/a_bit_of_everything_service.spec.js @@ -0,0 +1,258 @@ +'use strict'; + +var SwaggerClient = require('swagger-client'); + +describe('ABitOfEverythingService', function () { + var client; + + beforeEach(function (done) { + new SwaggerClient({ + url: "http://localhost:8080/openapiv2/a_bit_of_everything.swagger.json", + usePromise: true, + }).then(function (c) { + client = c; + }).catch(function (err) { + done.fail(err); + }).then(done); + }); + + describe('Create', function () { + var created; + var expected = { + floatValue: 1.5, + doubleValue: 2.5, + int64Value: "4294967296", + uint64Value: "9223372036854775807", + int32Value: -2147483648, + fixed64Value: "9223372036854775807", + fixed32Value: 4294967295, + boolValue: true, + stringValue: "strprefix/foo", + uint32Value: 4294967295, + sfixed32Value: 2147483647, + sfixed64Value: "-4611686018427387904", + sint32Value: 2147483647, + sint64Value: "4611686018427387903", + nonConventionalNameValue: "camelCase", + enumValue: "ONE", + pathEnumValue: "DEF", + nestedPathEnumValue: "JKL", + enumValueAnnotation: "ONE", + requiredStringViaFieldBehaviorAnnotation: "foo", + singleNested: null, + nested: [], + bytesValue: "", + repeatedStringValue: [], + mapValue: {}, + mappedStringValue: {}, + mappedNestedValue: {}, + timestampValue: "2006-01-02T15:04:05Z", + repeatedEnumValue: [], + repeatedEnumAnnotation: [], + repeatedStringAnnotation: [], + repeatedNestedAnnotation: [], + nestedAnnotation: null, + int64OverrideType: "0", + outputOnlyStringViaFieldBehaviorAnnotation: "", + }; + + beforeEach(function (done) { + client.ABitOfEverythingService.ABitOfEverythingService_Create(expected).then(function (resp) { + created = resp.obj; + }).catch(function (err) { + done.fail(err); + }).then(done); + }); + + it('should assign id', function () { + expect(created.uuid).not.toBe(""); + }); + + it('should echo the request back', function () { + delete created.uuid; + expect(created).toEqual(expected); + }); + }); + + describe('CreateBody', function () { + var created; + var expected = { + floatValue: 1.5, + doubleValue: 2.5, + int64Value: "4294967296", + uint64Value: "9223372036854775807", + int32Value: -2147483648, + fixed64Value: "9223372036854775807", + fixed32Value: 4294967295, + boolValue: true, + stringValue: "strprefix/foo", + uint32Value: 4294967295, + sfixed32Value: 2147483647, + sfixed64Value: "-4611686018427387904", + sint32Value: 2147483647, + sint64Value: "4611686018427387903", + nonConventionalNameValue: "camelCase", + enumValue: "ONE", + pathEnumValue: "DEF", + nestedPathEnumValue: "JKL", + nested: [ + { name: "bar", amount: 10 }, + { name: "baz", amount: 20 }, + ], + repeatedStringValue: ["a", "b", "c"], + oneofString: "x", + mapValue: { a: "ONE", b: 2 }, + mappedStringValue: { a: "x", b: "y" }, + mappedNestedValue: { + a: { name: "x", amount: 1 }, + b: { name: "y", amount: 2 }, + }, + enumValueAnnotation: "ONE", + requiredStringViaFieldBehaviorAnnotation: "foo", + singleNested: null, + nested: [], + bytesValue: "", + repeatedStringValue: [], + mapValue: {}, + mappedStringValue: {}, + mappedNestedValue: {}, + timestampValue: "2006-01-02T15:04:05Z", + repeatedEnumValue: [], + repeatedEnumAnnotation: [], + repeatedStringAnnotation: [], + repeatedNestedAnnotation: [], + nestedAnnotation: null, + int64OverrideType: "0", + outputOnlyStringViaFieldBehaviorAnnotation: "", + }; + + beforeEach(function (done) { + client.ABitOfEverythingService.ABitOfEverythingService_CreateBody({ + body: expected, + }).then(function (resp) { + created = resp.obj; + }).catch(function (err) { + done.fail(err); + }).then(done); + }); + + it('should assign id', function () { + expect(created.uuid).not.toBe(""); + }); + + it('should echo the request back', function () { + delete created.uuid; + expect(created).toEqual(expected); + }); + }); + + describe('lookup', function () { + var created; + var expected = { + boolValue: true, + stringValue: "strprefix/foo", + }; + + beforeEach(function (done) { + client.ABitOfEverythingService.ABitOfEverythingService_CreateBody({ + body: expected, + }).then(function (resp) { + created = resp.obj; + }).catch(function (err) { + fail(err); + }).finally(done); + }); + + it('should look up an object by uuid', function (done) { + client.ABitOfEverythingService.ABitOfEverythingService_Lookup({ + uuid: created.uuid + }).then(function (resp) { + expect(resp.obj).toEqual(created); + }).catch(function (err) { + fail(err.errObj); + }).finally(done); + }); + + it('should fail if no such object', function (done) { + client.ABitOfEverythingService.ABitOfEverythingService_Lookup({ + uuid: 'not_exist', + }).then(function (resp) { + fail('expected failure but succeeded'); + }).catch(function (err) { + expect(err.status).toBe(404); + }).finally(done); + }); + }); + + describe('Delete', function () { + var created; + var expected = { + boolValue: true, + stringValue: "strprefix/foo", + }; + + beforeEach(function (done) { + client.ABitOfEverythingService.ABitOfEverythingService_CreateBody({ + body: expected, + }).then(function (resp) { + created = resp.obj; + }).catch(function (err) { + fail(err); + }).finally(done); + }); + + it('should delete an object by id', function (done) { + client.ABitOfEverythingService.ABitOfEverythingService_Delete({ + uuid: created.uuid + }).then(function (resp) { + expect(resp.obj).toEqual({}); + }).catch(function (err) { + fail(err.errObj); + }).then(function () { + return client.ABitOfEverythingService.ABitOfEverythingService_Lookup({ + uuid: created.uuid + }); + }).then(function (resp) { + fail('expected failure but succeeded'); + }).catch(function (err) { + expect(err.status).toBe(404); + }).finally(done); + }); + }); + + describe('GetRepeatedQuery', function () { + var repeated; + var expected = { + pathRepeatedFloatValue: [1.5, -1.5], + pathRepeatedDoubleValue: [2.5, -2.5], + pathRepeatedInt64Value: ["4294967296", "-4294967296"], + pathRepeatedUint64Value: ["0", "9223372036854775807"], + pathRepeatedInt32Value: [2147483647, -2147483648], + pathRepeatedFixed64Value: ["0", "9223372036854775807"], + pathRepeatedFixed32Value: [0, 4294967295], + pathRepeatedBoolValue: [true, false], + pathRepeatedStringValue: ["foo", "bar"], + pathRepeatedBytesValue: ["AA==", "_w=="], + pathRepeatedUint32Value: [4294967295, 0], + pathRepeatedEnumValue: ["ONE", "ONE"], + pathRepeatedSfixed32Value: [-2147483648, 2147483647], + pathRepeatedSfixed64Value: ["-4294967296", "4294967296"], + pathRepeatedSint32Value: [2147483646, -2147483647], + pathRepeatedSint64Value: ["4611686018427387903", "-4611686018427387904"] + }; + + beforeEach(function (done) { + client.ABitOfEverythingService.ABitOfEverythingService_GetRepeatedQuery(expected).then(function (resp) { + repeated = resp.obj; + }).catch(function (err) { + done.fail(err); + }).then(done); + }); + + it('should echo the request back', function () { + // API will echo a non URL safe encoding + expected.pathRepeatedBytesValue = ["AA==", "/w=="]; + expect(repeated).toEqual(expected); + }); + }); +}); diff --git a/examples/browser/bin/.gitignore b/examples/internal/browser/bin/.gitignore similarity index 100% rename from examples/browser/bin/.gitignore rename to examples/internal/browser/bin/.gitignore diff --git a/examples/browser/bower.json b/examples/internal/browser/bower.json similarity index 86% rename from examples/browser/bower.json rename to examples/internal/browser/bower.json index 2454691ae08..f5a96c5931e 100644 --- a/examples/browser/bower.json +++ b/examples/internal/browser/bower.json @@ -1,6 +1,6 @@ { "name": "grpc-gateway-example-browser", - "description": "Example use of grpc-gateway from browser", + "description": "Example use of gRPC-Gateway from browser", "main": "index.js", "authors": [ "Yuki Yugui Sonoda " diff --git a/examples/internal/browser/echo_service.spec.js b/examples/internal/browser/echo_service.spec.js new file mode 100644 index 00000000000..01bef88b9a9 --- /dev/null +++ b/examples/internal/browser/echo_service.spec.js @@ -0,0 +1,53 @@ +'use strict'; + +var SwaggerClient = require('swagger-client'); + +describe('EchoService', function () { + var client; + + beforeEach(function (done) { + new SwaggerClient({ + url: "http://localhost:8080/openapiv2/echo_service.swagger.json", + usePromise: true, + }).then(function (c) { + client = c; + done(); + }); + }); + + describe('Echo', function () { + it('should echo the request back', function (done) { + var expected = { + id: "foo", + num: "0", + status: null + }; + client.EchoService.EchoService_Echo( + expected, + { responseContentType: "application/json" } + ).then(function (resp) { + expect(resp.obj).toEqual(expected); + }).catch(function (err) { + done.fail(err); + }).then(done); + }); + }); + + describe('EchoBody', function () { + it('should echo the request back', function (done) { + var expected = { + id: "foo", + num: "0", + status: null + }; + client.EchoService.EchoService_EchoBody( + { body: expected }, + { responseContentType: "application/json" } + ).then(function (resp) { + expect(resp.obj).toEqual(expected); + }).catch(function (err) { + done.fail(err); + }).then(done); + }); + }); +}); diff --git a/examples/browser/gulpfile.js b/examples/internal/browser/gulpfile.js similarity index 75% rename from examples/browser/gulpfile.js rename to examples/internal/browser/gulpfile.js index 233afed40e0..f287428d80e 100644 --- a/examples/browser/gulpfile.js +++ b/examples/internal/browser/gulpfile.js @@ -11,28 +11,28 @@ var shell = require('gulp-shell'); var jasmineBrowser = require('gulp-jasmine-browser'); var webpack = require('webpack-stream'); -gulp.task('bower', function(){ +gulp.task('bower', function () { return bower(); }); gulp.task('server', shell.task([ - 'go build -o bin/example-server github.com/grpc-ecosystem/grpc-gateway/examples/cmd/example-grpc-server', + 'go build -o bin/example-server github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/cmd/example-grpc-server', ])); gulp.task('gateway', shell.task([ - 'go build -o bin/example-gw github.com/grpc-ecosystem/grpc-gateway/examples/cmd/example-gateway-server', + 'go build -o bin/example-gw github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/cmd/example-gateway-server', ])); -gulp.task('serve-server', ['server'], function(){ +gulp.task('serve-server', ['server'], function () { gprocess.start('server-server', 'bin/example-server', [ - '--logtostderr', + '--logtostderr', ]); gulp.watch('bin/example-server', ['serve-server']); }); -gulp.task('serve-gateway', ['gateway', 'serve-server'], function(){ +gulp.task('serve-gateway', ['gateway', 'serve-server'], function () { gprocess.start('gateway-server', 'bin/example-gw', [ - '--logtostderr', '--swagger_dir', path.join(__dirname, "../proto/examplepb"), + '--logtostderr', '--openapi_dir', path.join(__dirname, "../proto/examplepb"), ]); gulp.watch('bin/example-gw', ['serve-gateway']); }); @@ -40,9 +40,9 @@ gulp.task('serve-gateway', ['gateway', 'serve-server'], function(){ gulp.task('backends', ['serve-gateway', 'serve-server']); var specFiles = ['*.spec.js']; -gulp.task('test', ['backends'], function(done) { +gulp.task('test', ['backends'], function (done) { return gulp.src(specFiles) - .pipe(webpack({output: {filename: 'spec.js'}})) + .pipe(webpack({ output: { filename: 'spec.js' } })) .pipe(jasmineBrowser.specRunner({ console: true, sourceMappedStacktrace: true, @@ -52,20 +52,20 @@ gulp.task('test', ['backends'], function(done) { catch: true, throwFailures: true, })) - .on('error', function(err) { + .on('error', function (err) { done(err); process.exit(1); }) .pipe(exit()); }); -gulp.task('serve', ['backends'], function(done) { +gulp.task('serve', ['backends'], function (done) { var JasminePlugin = require('gulp-jasmine-browser/webpack/jasmine-plugin'); var plugin = new JasminePlugin(); return gulp.src(specFiles) .pipe(webpack({ - output: {filename: 'spec.js'}, + output: { filename: 'spec.js' }, watch: true, plugins: [plugin], })) diff --git a/examples/browser/index.html b/examples/internal/browser/index.html similarity index 89% rename from examples/browser/index.html rename to examples/internal/browser/index.html index 7817451ca82..da9e223e3bf 100644 --- a/examples/browser/index.html +++ b/examples/internal/browser/index.html @@ -4,7 +4,7 @@