Skip to content

Commit 6d280aa

Browse files
Support generating unbound methods
Cherry picks 141dba3 from master. Co-authored-by: Philippe Laflamme <[email protected]>
1 parent 407cb8e commit 6d280aa

27 files changed

+2309
-72
lines changed

Makefile

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ GATEWAY_PLUGIN_FLAGS?=
4141
OPENAPI_PLUGIN_FLAGS?=
4242

4343
GOOGLEAPIS_DIR=third_party/googleapis
44-
OUTPUT_DIR=_output
4544

4645
OPENAPIV2_PROTO=protoc-gen-openapiv2/options/openapiv2.proto protoc-gen-openapiv2/options/annotations.proto
4746
OPENAPIV2_GO=$(OPENAPIV2_PROTO:.proto=.pb.go)
@@ -75,6 +74,7 @@ EXAMPLES=examples/internal/proto/examplepb/echo_service.proto \
7574
examples/internal/proto/examplepb/response_body_service.proto
7675

7776
STANDALONE_EXAMPLES=examples/internal/proto/examplepb/unannotated_echo_service.proto
77+
GENERATE_UNBOUND_METHODS_EXAMPLE=examples/internal/proto/examplepb/generate_unbound_methods.proto
7878

7979
HELLOWORLD=examples/internal/helloworld/helloworld.proto
8080

@@ -85,6 +85,10 @@ EXAMPLE_OPENAPIMERGESRCS=examples/internal/proto/examplepb/openapi_merge.swagger
8585
EXAMPLE_DEPS=examples/internal/proto/pathenum/path_enum.proto examples/internal/proto/sub/message.proto examples/internal/proto/sub2/message.proto
8686
EXAMPLE_DEPSRCS=$(EXAMPLE_DEPS:.proto=.pb.go)
8787

88+
GENERATE_UNBOUND_METHODS_EXAMPLE_OPENAPISRCS=$(GENERATE_UNBOUND_METHODS_EXAMPLE:.proto=.swagger.json)
89+
GENERATE_UNBOUND_METHODS_EXAMPLE_SVCSRCS=$(GENERATE_UNBOUND_METHODS_EXAMPLE:.proto=.pb.go)
90+
GENERATE_UNBOUND_METHODS_EXAMPLE_GWSRCS=$(GENERATE_UNBOUND_METHODS_EXAMPLE:.proto=.pb.gw.go)
91+
8892
HELLOWORLD_SVCSRCS=$(HELLOWORLD:.proto=.pb.go)
8993
HELLOWORLD_GWSRCS=$(HELLOWORLD:.proto=.pb.gw.go)
9094

@@ -140,8 +144,14 @@ RESPONSE_BODY_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/responsebody/client.go \
140144
$(EXAMPLE_CLIENT_DIR)/responsebody/model_examplepb_response_body_out_response.go \
141145
$(EXAMPLE_CLIENT_DIR)/responsebody/model_response_response_type.go \
142146
$(EXAMPLE_CLIENT_DIR)/responsebody/api_response_body_service.go
143-
144-
EXAMPLE_CLIENT_SRCS=$(ECHO_EXAMPLE_SRCS) $(ABE_EXAMPLE_SRCS) $(UNANNOTATED_ECHO_EXAMPLE_SRCS) $(RESPONSE_BODY_EXAMPLE_SRCS)
147+
GENERATE_UNBOUND_METHODS_EXAMPLE_SPEC=examples/internal/proto/examplepb/generate_unbound_methods.swagger.json
148+
GENERATE_UNBOUND_METHODS_EXAMPLE_SRCS=$(EXAMPLE_CLIENT_DIR)/generateunboundmethods/client.go \
149+
$(EXAMPLE_CLIENT_DIR)/generateunboundmethods/response.go \
150+
$(EXAMPLE_CLIENT_DIR)/generateunboundmethods/configuration.go \
151+
$(EXAMPLE_CLIENT_DIR)/generateunboundmethods/model_examplepb_generate_unbound_methods_simple_message.go \
152+
$(EXAMPLE_CLIENT_DIR)/generateunboundmethods/api_generate_unbound_methods.go
153+
154+
EXAMPLE_CLIENT_SRCS=$(ECHO_EXAMPLE_SRCS) $(ABE_EXAMPLE_SRCS) $(UNANNOTATED_ECHO_EXAMPLE_SRCS) $(RESPONSE_BODY_EXAMPLE_SRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_SRCS)
145155
SWAGGER_CODEGEN=swagger-codegen
146156

147157
PROTOC_INC_PATH=$(dir $(shell which protoc))/../include
@@ -189,6 +199,13 @@ $(EXAMPLE_OPENAPISRCS): $(OPENAPI_PLUGIN) $(OPENAPI_EXAMPLES)
189199
$(EXAMPLE_OPENAPIMERGESRCS): $(OPENAPI_PLUGIN) $(OPENAPIMERGE_EXAMPLES)
190200
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(OPENAPI_PLUGIN) --openapiv2_out=logtostderr=true,allow_repeated_fields_in_body=true,use_go_templates=true,allow_merge=true,merge_file_name=$(EXAMPLE_OPENAPIMERGESRCS:.swagger.json=):. $(OPENAPIMERGE_EXAMPLES)
191201

202+
$(GENERATE_UNBOUND_METHODS_EXAMPLE_GWSRCS): $(GATEWAY_PLUGIN) $(GENERATE_UNBOUND_METHODS_EXAMPLE)
203+
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GATEWAY_PLUGIN) --grpc-gateway_out=logtostderr=true,paths=source_relative,allow_repeated_fields_in_body=true,generate_unbound_methods=true$(ADDITIONAL_GW_FLAGS):. $(GENERATE_UNBOUND_METHODS_EXAMPLE)
204+
$(GENERATE_UNBOUND_METHODS_EXAMPLE_SVCSRCS): $(GO_PLUGIN) $(GENERATE_UNBOUND_METHODS_EXAMPLE)
205+
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --plugin=$(GO_GRPC_PLUGIN) --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. $(GENERATE_UNBOUND_METHODS_EXAMPLE)
206+
$(GENERATE_UNBOUND_METHODS_EXAMPLE_OPENAPISRCS): $(OPENAPI_PLUGIN) $(GENERATE_UNBOUND_METHODS_EXAMPLE)
207+
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(OPENAPI_PLUGIN) --openapiv2_out=logtostderr=true,allow_repeated_fields_in_body=true,use_go_templates=true,generate_unbound_methods=true:. $(GENERATE_UNBOUND_METHODS_EXAMPLE)
208+
192209
$(HELLOWORLD_SVCSRCS): $(GO_PLUGIN) $(GO_GRPC_PLUGIN) $(HELLOWORLD)
193210
protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GO_PLUGIN) --plugin=$(GO_GRPC_PLUGIN) --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. $(HELLOWORLD)
194211

@@ -217,8 +234,13 @@ $(RESPONSE_BODY_EXAMPLE_SRCS): $(RESPONSE_BODY_EXAMPLE_SPEC)
217234
-l go -o examples/internal/clients/responsebody --additional-properties packageName=responsebody
218235
@rm -f $(EXAMPLE_CLIENT_DIR)/responsebody/README.md \
219236
$(EXAMPLE_CLIENT_DIR)/responsebody/git_push.sh
237+
$(GENERATE_UNBOUND_METHODS_EXAMPLE_SRCS): $(GENERATE_UNBOUND_METHODS_EXAMPLE_SPEC)
238+
$(SWAGGER_CODEGEN) generate -i $(GENERATE_UNBOUND_METHODS_EXAMPLE_SPEC) \
239+
-l go -o examples/internal/clients/generateunboundmethods --additional-properties packageName=generateunboundmethods
240+
@rm -f $(EXAMPLE_CLIENT_DIR)/generateunboundmethods/README.md \
241+
$(EXAMPLE_CLIENT_DIR)/generateunboundmethods/git_push.sh
220242

221-
examples: $(EXAMPLE_DEPSRCS) $(EXAMPLE_SVCSRCS) $(EXAMPLE_GWSRCS) $(EXAMPLE_OPENAPISRCS) $(EXAMPLE_OPENAPIMERGESRCS) $(EXAMPLE_CLIENT_SRCS) $(HELLOWORLD_SVCSRCS) $(HELLOWORLD_GWSRCS)
243+
examples: $(EXAMPLE_DEPSRCS) $(EXAMPLE_SVCSRCS) $(EXAMPLE_GWSRCS) $(EXAMPLE_OPENAPISRCS) $(EXAMPLE_OPENAPIMERGESRCS) $(EXAMPLE_CLIENT_SRCS) $(HELLOWORLD_SVCSRCS) $(HELLOWORLD_GWSRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_GWSRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_SVCSRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_OPENAPISRCS)
222244
testproto: $(RUNTIME_TEST_SRCS) $(APICONFIG_SRCS)
223245
test: examples testproto
224246
go test -short -race ./...
@@ -248,6 +270,9 @@ realclean: distclean
248270
rm -f $(EXAMPLE_GWSRCS)
249271
rm -f $(EXAMPLE_OPENAPISRCS)
250272
rm -f $(EXAMPLE_CLIENT_SRCS)
273+
rm -f $(GENERATE_UNBOUND_METHODS_EXAMPLE_GWSRCS)
274+
rm -f $(GENERATE_UNBOUND_METHODS_EXAMPLE_SVCSRCS)
275+
rm -f $(GENERATE_UNBOUND_METHODS_EXAMPLE_OPENAPISRCS)
251276
rm -f $(HELLOWORLD_SVCSRCS)
252277
rm -f $(HELLOWORLD_GWSRCS)
253278
rm -f $(OPENAPIV2_GO)

README.md

Lines changed: 93 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,60 @@ Make sure that your `$GOBIN` is in your `$PATH`.
9797
}
9898
```
9999

100-
2. Add a [`google.api.http`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L46)
101-
annotation to your .proto file
100+
2. Generate gRPC stubs
101+
102+
This step generates the gRPC stubs that you can use to implement the service and consume from clients:
103+
104+
Here's an example of what a `protoc` command might look like to generate Go stubs:
105+
106+
```sh
107+
protoc -I . --go_out ./gen/go/ --go_opt plugins=grpc --go_opt paths=source_relative your/service/v1/your_service.proto
108+
```
109+
110+
3. Implement your service in gRPC as usual
111+
112+
1. (Optional) Generate gRPC stub in the [other programming languages](https://grpc.io/docs/).
113+
114+
For example, the following generates gRPC code for Ruby based on `your/service/v1/your_service.proto`:
115+
```sh
116+
protoc -I . --ruby_out ./gen/ruby your/service/v1/your_service.proto
117+
118+
protoc -I . --grpc-ruby_out ./gen/ruby your/service/v1/your_service.proto
119+
```
120+
2. Add the googleapis-common-protos gem (or your language equivalent) as a dependency to your project.
121+
3. Implement your gRPC service stubs
122+
123+
4. Generate reverse-proxy using `protoc-gen-grpc-gateway`
124+
125+
At this point, you have 3 options:
126+
127+
* no further modifications, use the default mapping to HTTP semantics (method, path, etc.)
128+
* this will work on any `.proto` file, but will not allow setting HTTP paths, request parameters or similar
129+
* additional `.proto` modifications to use a custom mapping
130+
* relies on parameters in the `.proto` file to set custom HTTP mappings
131+
* no `.proto` modifications, but use an external configuration file
132+
* relies on an external configuration file to set custom HTTP mappings
133+
* mostly useful when the source proto file isn't under your control
134+
135+
1. Using the default mapping
136+
137+
This requires no additional modification to the `.proto` file, but does require enabling a specific option when executing the plugin.
138+
The `generate_unbound_methods` should be enabled.
139+
140+
Here's what a `protoc` execution might look like with this option enabled:
141+
142+
```sh
143+
protoc -I . --grpc-gateway_out ./gen/go \
144+
--grpc-gateway_opt logtostderr=true \
145+
--grpc-gateway_opt paths=source_relative \
146+
--grpc-gateway_opt generate_unbound_methods=true \
147+
your/service/v1/your_service.proto
148+
```
149+
150+
2. With custom annotations
151+
152+
Add a [`google.api.http`](https://github.com/googleapis/googleapis/blob/master/google/api/http.proto#L46)
153+
annotation to your .proto file
102154

103155
`your_service.proto`:
104156
```diff
@@ -131,68 +183,34 @@ annotation to your .proto file
131183
See [a_bit_of_everything.proto](examples/internal/proto/examplepb/a_bit_of_everything.proto)
132184
for examples of more annotations you can add to customize gateway behavior
133185
and generated OpenAPI output.
186+
187+
Here's what a `protoc` execution might look like:
134188
135-
If you do not want to modify the proto file for use with grpc-gateway you can
189+
```sh
190+
protoc -I . --grpc-gateway_out ./gen/go \
191+
--grpc-gateway_opt logtostderr=true \
192+
--grpc-gateway_opt paths=source_relative \
193+
your/service/v1/your_service.proto
194+
```
195+
196+
3. External configuration
197+
If you do not want to (or cannot) modify the proto file for use with grpc-gateway you can
136198
alternatively use an external
137199
[gRPC Service Configuration](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config) file.
138200
[Check our documentation](https://grpc-ecosystem.github.io/grpc-gateway/docs/grpcapiconfiguration.html)
139201
for more information.
140202
141-
3. Generate gRPC stub
142-
143-
You will need to provide the required third party protobuf files to the `protoc` compiler.
144-
They are included in this repo under the `third_party/googleapis` folder, and we recommend copying
145-
them into your `protoc` generation file structure. If you've structured your protofiles according
146-
to something like [the Buf style guide](https://buf.build/docs/style-guide#files-and-packages),
147-
you could copy the files into a top-level `./google` folder.
148-
149-
Here is an example of what a `protoc` command might look like:
150-
151-
```sh
152-
protoc -I. --go_out=plugins=grpc,paths=source_relative:./gen/go/ your/service/v1/your_service.proto
153-
```
203+
Here's what a `protoc` execution might look like with this option enabled:
154204
155-
It will generate a stub file with path `./gen/go/your/service/v1/your_service.pb.go`.
205+
```sh
206+
protoc -I . --grpc-gateway_out ./gen/go \
207+
--grpc-gateway_opt logtostderr=true \
208+
--grpc-gateway_opt paths=source_relative \
209+
--grpc-gateway_opt grpc_api_configuration=path/to/config.yaml \
210+
your/service/v1/your_service.proto
211+
```
156212
157-
4. Implement your service in gRPC as usual
158-
159-
1. (Optional) Generate gRPC stub in the [other programming languages](https://grpc.io/docs/).
160-
161-
For example, the following generates gRPC code for Ruby based on `your/service/v1/your_service.proto`:
162-
```sh
163-
protoc -I. --ruby_out=./gen/ruby your/service/v1/your_service.proto
164-
165-
protoc -I. --grpc-ruby_out=./gen/ruby your/service/v1/your_service.proto
166-
```
167-
2. Add the googleapis-common-protos gem (or your language equivalent) as a dependency to your project.
168-
3. Implement your gRPC service stubs
169-
170-
5. Generate reverse-proxy using `protoc-gen-grpc-gateway`
171-
172-
```sh
173-
protoc -I. --grpc-gateway_out=logtostderr=true,paths=source_relative:./gen/go \
174-
your/service/v1/your_service.proto
175-
```
176-
177-
It will generate a reverse proxy `gen/go/your/service/v1/your_service.pb.gw.go`.
178-
179-
OR generate a standalone reverse-proxy if needed.
180-
181-
Suppose you have a generated gRPC stub package, and you want to deploy several
182-
API gateways using client-specific
183-
[YAML annotations](https://grpc-ecosystem.github.io/grpc-gateway/docs/grpcapiconfiguration.html).
184-
You can generate a grpc-gateway which imports the stub as an external
185-
package, so you don't have to regenerate it several times.
186-
To set the import path of the stub package, set its full path in the `go_package`.
187-
188-
```sh
189-
protoc -I. --grpc-gateway_out=logtostderr=true,grpc_api_configuration=apiOne.yaml,paths=source_relative,standalone=true:./gen/go/client_one \
190-
your/service/v1/your_service.proto
191-
192-
protoc -I. --grpc-gateway_out=logtostderr=true,grpc_api_configuration=apiTwo.yaml,paths=source_relative,standalone=true:./gen/go/client_two \
193-
your/service/v1/your_service.proto
194-
195-
6. Write an entrypoint for the HTTP reverse-proxy server
213+
5. Write an entrypoint for the HTTP reverse-proxy server
196214
197215
```go
198216
package main
@@ -243,12 +261,14 @@ annotation to your .proto file
243261
}
244262
```
245263
246-
7. (Optional) Generate OpenAPI definitions using `protoc-gen-openapiv2`
264+
6. (Optional) Generate OpenAPI definitions using `protoc-gen-openapiv2`
247265
248266
```sh
249-
protoc -I. --openapiv2_out=logtostderr=true:./gen/openapiv2 your/service/v1/your_service.proto
267+
protoc -I . --openapiv2_out ./gen/openapiv2 --openapiv2_opt logtostderr=true your/service/v1/your_service.proto
250268
```
251269
270+
Note that this plugin also supports generating swagger definitions for unannotated methods; use the `generate_unbound_methods` option to enable this.
271+
252272
## Video intro
253273
254274
This GopherCon UK 2019 presentation from our maintainer
@@ -261,14 +281,27 @@ https://github.com/johanbrandhorst/grpc-gateway-boilerplate.
261281
## Parameters and flags
262282
263283
During code generation with `protoc`, flags to grpc-gateway tools must be passed
264-
through protoc using the `--<tool_suffix>_out=<flags>:<path>` pattern, for
265-
example:
284+
through protoc using one of 2 patterns:
285+
286+
* as part of the `--<tool_suffix>_out` `protoc` parameter: `--<tool_suffix>_out=<flags>:<path>`
266287
267288
```sh
268289
--grpc-gateway_out=logtostderr=true,repeated_path_param_separator=ssv:.
269290
--openapiv2_out=logtostderr=true,repeated_path_param_separator=ssv:.
270291
```
271292
293+
* using additional `--<tool_suffix>_opt` parameters: `--<tool_suffix>_opt=<flag>[,<flag>]*`
294+
295+
```sh
296+
--grpc-gateway_opt logtostderr=true,repeated_path_param_separator=ssv
297+
# or separately
298+
--grpc-gateway_opt logtostderr=true --grpc-gateway_opt repeated_path_param_separator=ssv
299+
300+
--openapiv2_opt logtostderr=true,repeated_path_param_separator=ssv
301+
# or separately
302+
--openapiv2_opt logtostderr=true --openapiv2_opt repeated_path_param_separator=ssv
303+
```
304+
272305
`protoc-gen-grpc-gateway` supports custom mapping from Protobuf `import` to
273306
Golang import paths. They are compatible with
274307
[the parameters with the same names in `protoc-gen-go`](https://github.com/golang/protobuf#parameters).

docs/_docs/grpcapiconfiguration.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
11
---
2-
title: Usage without annotations (gRPC API Configuration)
2+
title: Usage without annotations
33
category: documentation
44
order: 100
55
---
66

77
# gRPC API Configuration
8-
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.
8+
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.
99

10+
`grpc-gateway` supports 2 ways of dealing with these situations:
11+
12+
* [use the `generate_unbound_methods` option](#generate_unbound_methods)
13+
* [provide an external configuration file](#using-an-external-configuration-file) (gRPC API Configuration)
14+
15+
## `generate_unbound_methods`
16+
17+
Providing this parameter to the protoc plugin will make it produce the HTTP mapping even for methods without any `HttpRule` annotation.
18+
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:
19+
20+
* HTTP method is `POST`
21+
* URI path is built from the service's name and method: `/<fully qualified service name>/<method name>` (e.g.: `/my.package.EchoService/Echo`)
22+
* HTTP body is the serialized protobuf message.
23+
24+
NOTE: the same option is also supported by the `gen-swagger` plugin.
25+
26+
## Using an external configuration file
1027
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.
1128

1229
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.
1330

1431
OpenAPI options may also be configured via ["OpenAPI Configuration"](https://github.com/grpc-ecosystem/grpc-gateway/tree/v2/internal/descriptor/openapiconfig/openapiconfig.proto) in the form of YAML configuration files.
1532

16-
## Usage of gRPC API Configuration YAML files
33+
### Usage of gRPC API Configuration YAML files
1734
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:
1835

1936
1. Define your service in gRPC as usual
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/docs
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.gitignore
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2.4.8
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library")
2+
3+
go_library(
4+
name = "go_default_library",
5+
srcs = [
6+
"api_generate_unbound_methods_echo_service.go",
7+
"client.go",
8+
"configuration.go",
9+
"model_examplepb_generate_unbound_methods_simple_message.go",
10+
"model_protobuf_any.go",
11+
"model_rpc_status.go",
12+
"model_runtime_error.go",
13+
"response.go",
14+
],
15+
importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/clients/generateunboundmethods",
16+
visibility = ["//examples:__subpackages__"],
17+
deps = ["@org_golang_x_oauth2//:go_default_library"],
18+
)

0 commit comments

Comments
 (0)