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

Fixes #407 and #1700 - google.api.http path parameter constraints and multiple HTTP path/method endpoint support #2461

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,19 @@ Alternatively, see the section on remotely managed plugin versions below.
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). 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
Expand Down Expand Up @@ -571,6 +584,8 @@ But patches are welcome.
(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 [RFC 6570](https://tools.ietf.org/html/rfc6570)
path template syntax. For example, `/api/v1/{name=projects/*/topics/*}` or `/prefix/{path=organizations/**}`.

## Contribution

Expand Down
11 changes: 9 additions & 2 deletions examples/internal/clients/abe/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ paths:
in: "query"
required: false
type: "string"
pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
x-exportParamName: "Uuid"
x-optionalDataType: "String"
- name: "floatValue"
Expand Down Expand Up @@ -470,6 +471,7 @@ paths:
in: "query"
required: false
type: "string"
pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
x-exportParamName: "Uuid"
x-optionalDataType: "String"
- name: "floatValue"
Expand Down Expand Up @@ -773,6 +775,7 @@ paths:
in: "query"
required: false
type: "string"
pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
x-exportParamName: "Uuid"
x-optionalDataType: "String"
- name: "floatValue"
Expand Down Expand Up @@ -1445,6 +1448,7 @@ paths:
in: "path"
required: true
type: "string"
pattern: "strprefix/[^/]+"
x-exportParamName: "StringValue"
- name: "uint32Value"
in: "path"
Expand Down Expand Up @@ -1544,6 +1548,7 @@ paths:
in: "query"
required: false
type: "string"
pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
x-exportParamName: "Uuid"
x-optionalDataType: "String"
- name: "bytesValue"
Expand Down Expand Up @@ -2076,7 +2081,7 @@ paths:
description: "An unexpected error response."
schema:
$ref: "#/definitions/rpcStatus"
/v1/{book.name=publishers/*/books/*}:
/v1/{book.name}:
patch:
tags:
- "ABitOfEverythingService"
Expand All @@ -2088,6 +2093,7 @@ paths:
\nExample: `publishers/1257894000000000000/books/my-book`"
required: true
type: "string"
pattern: "publishers/[^/]+/books/[^/]+"
x-exportParamName: "BookName"
- in: "body"
name: "body"
Expand Down Expand Up @@ -2138,7 +2144,7 @@ paths:
description: "An unexpected error response."
schema:
$ref: "#/definitions/rpcStatus"
/v1/{parent=publishers/*}/books:
/v1/{parent}/books:
post:
tags:
- "ABitOfEverythingService"
Expand All @@ -2151,6 +2157,7 @@ paths:
\nExample: `publishers/1257894000000000000`"
required: true
type: "string"
pattern: "publishers/[^/]+"
x-exportParamName: "Parent"
- in: "body"
name: "body"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1943,7 +1943,7 @@ func (a *ABitOfEverythingServiceApiService) ABitOfEverythingServiceCreateBook(ct
)

// create path and map variables
localVarPath := a.client.cfg.BasePath + "/v1/{parent=publishers/*}/books"
localVarPath := a.client.cfg.BasePath + "/v1/{parent}/books"
localVarPath = strings.Replace(localVarPath, "{"+"parent"+"}", fmt.Sprintf("%v", parent), -1)

localVarHeaderParams := make(map[string]string)
Expand Down Expand Up @@ -4080,7 +4080,7 @@ func (a *ABitOfEverythingServiceApiService) ABitOfEverythingServiceUpdateBook(ct
)

// create path and map variables
localVarPath := a.client.cfg.BasePath + "/v1/{book.name=publishers/*/books/*}"
localVarPath := a.client.cfg.BasePath + "/v1/{book.name}"
localVarPath = strings.Replace(localVarPath, "{"+"book.name"+"}", fmt.Sprintf("%v", bookName), -1)

localVarHeaderParams := make(map[string]string)
Expand Down
25 changes: 16 additions & 9 deletions examples/internal/proto/examplepb/a_bit_of_everything.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@
"name": "uuid",
"in": "query",
"required": false,
"type": "string"
"type": "string",
"pattern": "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
},
{
"name": "floatValue",
Expand Down Expand Up @@ -579,7 +580,8 @@
"name": "uuid",
"in": "query",
"required": false,
"type": "string"
"type": "string",
"pattern": "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
},
{
"name": "floatValue",
Expand Down Expand Up @@ -913,7 +915,8 @@
"name": "uuid",
"in": "query",
"required": false,
"type": "string"
"type": "string",
"pattern": "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
},
{
"name": "floatValue",
Expand Down Expand Up @@ -1652,7 +1655,8 @@
"name": "stringValue",
"in": "path",
"required": true,
"type": "string"
"type": "string",
"pattern": "strprefix/[^/]+"
},
{
"name": "uint32Value",
Expand Down Expand Up @@ -1766,7 +1770,8 @@
"name": "uuid",
"in": "query",
"required": false,
"type": "string"
"type": "string",
"pattern": "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
},
{
"name": "bytesValue",
Expand Down Expand Up @@ -2615,7 +2620,7 @@
]
}
},
"/v1/{book.name=publishers/*/books/*}": {
"/v1/{book.name}": {
"patch": {
"operationId": "ABitOfEverythingService_UpdateBook",
"responses": {
Expand Down Expand Up @@ -2661,7 +2666,8 @@
"description": "The resource name of the book.\n\nFormat: `publishers/{publisher}/books/{book}`\n\nExample: `publishers/1257894000000000000/books/my-book`",
"in": "path",
"required": true,
"type": "string"
"type": "string",
"pattern": "publishers/[^/]+/books/[^/]+"
},
{
"name": "body",
Expand Down Expand Up @@ -2692,7 +2698,7 @@
]
}
},
"/v1/{parent=publishers/*}/books": {
"/v1/{parent}/books": {
"post": {
"summary": "Create a book.",
"operationId": "ABitOfEverythingService_CreateBook",
Expand Down Expand Up @@ -2739,7 +2745,8 @@
"description": "The publisher in which to create the book.\n\nFormat: `publishers/{publisher}`\n\nExample: `publishers/1257894000000000000`",
"in": "path",
"required": true,
"type": "string"
"type": "string",
"pattern": "publishers/[^/]+"
},
{
"name": "body",
Expand Down
Loading