Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate unbound methods #1652

Merged
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 30 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ EXAMPLES=examples/internal/proto/examplepb/echo_service.proto \
examples/internal/proto/examplepb/use_go_template.proto \
examples/internal/proto/examplepb/response_body_service.proto

GENERATE_UNBOUND_METHODS_EXAMPLE=examples/internal/proto/examplepb/generate_unbound_methods.proto

HELLOWORLD=examples/internal/helloworld/helloworld.proto

EXAMPLE_SVCSRCS=$(EXAMPLES:.proto=.pb.go)
Expand All @@ -88,6 +90,10 @@ EXAMPLE_SWAGGER_MERGE=examples/internal/proto/examplepb/swagger_merge.swagger.js
EXAMPLE_DEPS=examples/internal/proto/pathenum/path_enum.proto examples/internal/proto/sub/message.proto examples/internal/proto/sub2/message.proto
EXAMPLE_DEPSRCS=$(EXAMPLE_DEPS:.proto=.pb.go)

GENERATE_UNBOUND_METHODS_EXAMPLE_SWAGGERSRCS=$(GENERATE_UNBOUND_METHODS_EXAMPLE:.proto=.swagger.json)
GENERATE_UNBOUND_METHODS_EXAMPLE_SVCSRCS=$(GENERATE_UNBOUND_METHODS_EXAMPLE:.proto=.pb.go)
GENERATE_UNBOUND_METHODS_EXAMPLE_GWSRCS=$(GENERATE_UNBOUND_METHODS_EXAMPLE:.proto=.pb.gw.go)

HELLOWORLD_SVCSRCS=$(HELLOWORLD:.proto=.pb.go)
HELLOWORLD_GWSRCS=$(HELLOWORLD:.proto=.pb.gw.go)

Expand Down Expand Up @@ -136,8 +142,14 @@ RESPONSE_BODY_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/responsebody/client.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

EXAMPLE_CLIENT_SRCS=$(ECHO_EXAMPLE_SRCS) $(ABE_EXAMPLE_SRCS) $(UNANNOTATED_ECHO_EXAMPLE_SRCS) $(RESPONSE_BODY_EXAMPLE_SRCS)
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)
johanbrandhorst marked this conversation as resolved.
Show resolved Hide resolved
SWAGGER_CODEGEN=swagger-codegen

PROTOC_INC_PATH=$(dir $(shell which protoc))/../include
Expand Down Expand Up @@ -182,6 +194,13 @@ $(EXAMPLE_SWAGGERSRCS): $(SWAGGER_PLUGIN) $(SWAGGER_EXAMPLES)
$(EXAMPLE_SWAGGER_MERGE): $(SWAGGER_PLUGIN) $(SWAGGER_EXAMPLE_MERGE)
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(SWAGGER_PLUGIN) --swagger_out=logtostderr=true,allow_repeated_fields_in_body=true,use_go_templates=true,allow_merge=true,merge_file_name=$(EXAMPLE_SWAGGER_MERGE:.swagger.json=),$(PKGMAP):. $(SWAGGER_EXAMPLE_MERGE)

$(GENERATE_UNBOUND_METHODS_EXAMPLE_GWSRCS): $(GATEWAY_PLUGIN) $(GENERATE_UNBOUND_METHODS_EXAMPLE)
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GATEWAY_PLUGIN) --grpc-gateway_out=logtostderr=true,allow_repeated_fields_in_body=true,generate_unbound_methods=true,$(PKGMAP)$(ADDITIONAL_GW_FLAGS):. $(GENERATE_UNBOUND_METHODS_EXAMPLE)
$(GENERATE_UNBOUND_METHODS_EXAMPLE_SVCSRCS): $(GO_PLUGIN) $(GENERATE_UNBOUND_METHODS_EXAMPLE)
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP),plugins=grpc,paths=source_relative:. $(GENERATE_UNBOUND_METHODS_EXAMPLE)
$(GENERATE_UNBOUND_METHODS_EXAMPLE_SWAGGERSRCS): $(SWAGGER_PLUGIN) $(GENERATE_UNBOUND_METHODS_EXAMPLE)
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(SWAGGER_PLUGIN) --swagger_out=logtostderr=true,allow_repeated_fields_in_body=true,use_go_templates=true,generate_unbound_methods=true,$(PKGMAP):. $(GENERATE_UNBOUND_METHODS_EXAMPLE)

$(HELLOWORLD_SVCSRCS): $(GO_PLUGIN) $(HELLOWORLD)
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP),plugins=grpc,paths=source_relative:. $(HELLOWORLD)

Expand Down Expand Up @@ -210,8 +229,13 @@ $(RESPONSE_BODY_EXAMPLE_SRCS): $(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
johanbrandhorst marked this conversation as resolved.
Show resolved Hide resolved

examples: $(EXAMPLE_DEPSRCS) $(EXAMPLE_SVCSRCS) $(EXAMPLE_GWSRCS) $(EXAMPLE_SWAGGERSRCS) $(EXAMPLE_SWAGGER_MERGE) $(EXAMPLE_CLIENT_SRCS) $(HELLOWORLD_SVCSRCS) $(HELLOWORLD_GWSRCS)
examples: $(EXAMPLE_DEPSRCS) $(EXAMPLE_SVCSRCS) $(EXAMPLE_GWSRCS) $(EXAMPLE_SWAGGERSRCS) $(EXAMPLE_SWAGGER_MERGE) $(EXAMPLE_CLIENT_SRCS) $(HELLOWORLD_SVCSRCS) $(HELLOWORLD_GWSRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_GWSRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_SVCSRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_SWAGGERSRCS)
testproto: $(RUNTIME_TEST_SRCS)
test: examples testproto
go test -short -race ./...
Expand Down Expand Up @@ -250,6 +274,9 @@ realclean: distclean
rm -f $(EXAMPLE_GWSRCS)
rm -f $(EXAMPLE_SWAGGERSRCS)
rm -f $(EXAMPLE_CLIENT_SRCS)
rm -f $(GENERATE_UNBOUND_METHODS_EXAMPLE_GWSRCS)
rm -f $(GENERATE_UNBOUND_METHODS_EXAMPLE_SVCSRCS)
rm -f $(GENERATE_UNBOUND_METHODS_EXAMPLE_SWAGGERSRCS)
rm -f $(HELLOWORLD_SVCSRCS)
rm -f $(HELLOWORLD_GWSRCS)
rm -f $(OPENAPIV2_GO)
Expand Down
106 changes: 71 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,58 @@ Make sure that your `$GOBIN` is in your `$PATH`.
}
```

2. Add a [`google.api.http`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L46)
annotation to your .proto file
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 of what a `protoc` command might look like to generate Go stubs:

```sh
protoc -I. --go_out=plugins=grpc,paths=source_relative:./gen/go/ your/service/v1/your_service.proto
```
plaflamme marked this conversation as resolved.
Show resolved Hide resolved

3. Implement your service in gRPC as usual

1. (Optional) Generate gRPC stub in the [other programming languages](https://grpc.io/docs/).

For example, the following generates gRPC code for Ruby based on `your/service/v1/your_service.proto`:
```sh
protoc -I. --ruby_out=./gen/ruby your/service/v1/your_service.proto
plaflamme marked this conversation as resolved.
Show resolved Hide resolved

protoc -I. --grpc-ruby_out=./gen/ruby your/service/v1/your_service.proto
plaflamme marked this conversation as resolved.
Show resolved Hide resolved
```
2. Add the googleapis-common-protos gem (or your language equivalent) as a dependency to your project.
3. Implement your gRPC service stubs

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 `protoc` execution might look like with this option enabled:

```sh
protoc -I. --grpc-gateway_out=logtostderr=true,paths=source_relative:./gen/go \
--grpc-gateway_opt=generate_unbound_methods=true \
your/service/v1/your_service.proto
plaflamme marked this conversation as resolved.
Show resolved Hide resolved
```

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
Expand Down Expand Up @@ -130,46 +180,30 @@ annotation to your .proto file
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 Swagger output.

Here's what a `protoc` execution might look like:

```sh
protoc -I. --grpc-gateway_out=logtostderr=true,paths=source_relative:./gen/go \
your/service/v1/your_service.proto
plaflamme marked this conversation as resolved.
Show resolved Hide resolved
```

If you do not want to modify the proto file for use with grpc-gateway you can
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/grpcapiconfiguration.html)
for more information.

3. Generate gRPC stub

Here is an example of what a `protoc` command might look like:

```sh
protoc -I. --go_out=plugins=grpc,paths=source_relative:./gen/go/ 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

1. (Optional) Generate gRPC stub in the [other programming languages](https://grpc.io/docs/).

For example, the following generates gRPC code for Ruby based on `your/service/v1/your_service.proto`:
```sh
protoc -I. --ruby_out=./gen/ruby your/service/v1/your_service.proto

protoc -I. --grpc-ruby_out=./gen/ruby your/service/v1/your_service.proto
```
2. Add the googleapis-common-protos gem (or your language equivalent) as a dependency to your project.
3. Implement your gRPC service stubs
Here's what a `protoc` execution might look like with this option enabled:

5. Generate reverse-proxy using `protoc-gen-grpc-gateway`
```sh
protoc -I. --grpc-gateway_out=logtostderr=true,paths=source_relative:./gen/go \
--grpc-gateway_opt=grpc_api_configuration=path/to/config.yaml \
your/service/v1/your_service.proto
plaflamme marked this conversation as resolved.
Show resolved Hide resolved
```

```sh
protoc -I. --grpc-gateway_out=logtostderr=true,paths=source_relative:./gen/go \
your/service/v1/your_service.proto
```

It will generate a reverse proxy `gen/go/your/service/v1/your_service.pb.gw.go`.

6. Write an entrypoint for the HTTP reverse-proxy server
5. Write an entrypoint for the HTTP reverse-proxy server

```go
package main
Expand Down Expand Up @@ -220,12 +254,14 @@ annotation to your .proto file
}
```

7. (Optional) Generate swagger definitions using `protoc-gen-swagger`
6. (Optional) Generate swagger definitions using `protoc-gen-swagger`

```sh
protoc -I. --swagger_out=logtostderr=true:./gen/swagger your/service/v1/your_service.proto
```
plaflamme marked this conversation as resolved.
Show resolved Hide resolved

Note that this plugin also supports generating swagger definitions for unannotated methods; use the `generate_unbound_methods` option to enable this.

## Video intro

This GopherCon UK 2019 presentation from our maintainer
Expand Down
23 changes: 20 additions & 3 deletions docs/_docs/grpcapiconfiguration.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
---
title: Usage without annotations (gRPC API Configuration)
title: Usage without annotations
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.
In some situations 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.

`grpc-gateway` supports 2 ways of dealing with these situations:

* [use the `generate_unbound_methods` option](#generate_unbound_methods)
* [provide an external configuration file](#using-an-external-configuration-file) (gRPC API Configuration)

## `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: `/<fully qualified service name>/<method name>` (e.g.: `/my.package.EchoService/Echo`)
* HTTP body is the serialized protobuf message.

NOTE: the same option is also supported by the `gen-swagger` 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 a annotated proto file with the grpc-gateway generators.

## Usage of gRPC API Configuration YAML files
### 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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/docs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.4.8
17 changes: 17 additions & 0 deletions examples/internal/clients/generateunboundmethods/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = [
"api_generate_unbound_methods_echo_service.go",
"client.go",
"configuration.go",
"model_examplepb_generate_unbound_methods_simple_message.go",
"model_protobuf_any.go",
"model_runtime_error.go",
"response.go",
],
importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/internal/clients/generateunboundmethods",
visibility = ["//examples:__subpackages__"],
deps = ["@org_golang_x_oauth2//:go_default_library"],
)