From a0500cba2195de914ae92997961c957aac0082f2 Mon Sep 17 00:00:00 2001 From: Alex Bice Date: Thu, 17 Jan 2019 15:35:55 -0700 Subject: [PATCH] Support outputting arrays in protoc-gen-swagger (#853) * protoc-gen-swagger: add flag 'allow_repeated_fields_in_body' Also added/updated tests for flags * protoc-gen-swagger: updated response body example Also: * Added integration tests * Added an extra cli test for parsing the flag --- Makefile | 4 +- examples/clients/responsebody/BUILD.bazel | 3 + .../docs/ExamplepbRepeatedResponseBodyOut.md | 10 + ...xamplepbRepeatedResponseBodyOutResponse.md | 10 + .../docs/ExamplepbRepeatedResponseStrings.md | 10 + .../docs/ResponseBodyServiceApi.md | 56 ++++ .../examplepb_repeated_response_body_out.go | 16 ++ ...epb_repeated_response_body_out_response.go | 16 ++ .../examplepb_repeated_response_strings.go | 16 ++ .../responsebody/response_body_service_api.go | 120 +++++++++ examples/integration/integration_test.go | 50 ++++ .../examplepb/response_body_service.pb.go | 240 ++++++++++++++++-- .../examplepb/response_body_service.pb.gw.go | 120 +++++++++ .../examplepb/response_body_service.proto | 43 +++- .../response_body_service.swagger.json | 84 ++++++ examples/server/responsebody.go | 16 ++ protoc-gen-grpc-gateway/BUILD.bazel | 5 +- protoc-gen-swagger/defs.bzl | 2 +- protoc-gen-swagger/main.go | 9 + protoc-gen-swagger/main_test.go | 209 ++++++++------- 20 files changed, 901 insertions(+), 138 deletions(-) create mode 100644 examples/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOut.md create mode 100644 examples/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOutResponse.md create mode 100644 examples/clients/responsebody/docs/ExamplepbRepeatedResponseStrings.md create mode 100644 examples/clients/responsebody/examplepb_repeated_response_body_out.go create mode 100644 examples/clients/responsebody/examplepb_repeated_response_body_out_response.go create mode 100644 examples/clients/responsebody/examplepb_repeated_response_strings.go diff --git a/Makefile b/Makefile index 23b720ad4a5..8d7156cc378 100644 --- a/Makefile +++ b/Makefile @@ -149,11 +149,11 @@ $(EXAMPLE_DEPSRCS): $(GO_PLUGIN) $(EXAMPLE_DEPS) $(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) + protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GATEWAY_PLUGIN) --grpc-gateway_out=logtostderr=true,allow_repeated_fields_in_body=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) + protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(SWAGGER_PLUGIN) --swagger_out=logtostderr=true,allow_repeated_fields_in_body=true,$(PKGMAP)$(ADDITIONAL_SWG_FLAGS):. $(SWAGGER_EXAMPLES) $(ECHO_EXAMPLE_SRCS): $(ECHO_EXAMPLE_SPEC) $(SWAGGER_CODEGEN) generate -i $(ECHO_EXAMPLE_SPEC) \ diff --git a/examples/clients/responsebody/BUILD.bazel b/examples/clients/responsebody/BUILD.bazel index bdbc237a815..393d28446e4 100644 --- a/examples/clients/responsebody/BUILD.bazel +++ b/examples/clients/responsebody/BUILD.bazel @@ -6,6 +6,9 @@ go_library( "api_client.go", "api_response.go", "configuration.go", + "examplepb_repeated_response_body_out.go", + "examplepb_repeated_response_body_out_response.go", + "examplepb_repeated_response_strings.go", "examplepb_response_body_message.go", "examplepb_response_body_message_response.go", "examplepb_response_body_out.go", diff --git a/examples/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOut.md b/examples/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOut.md new file mode 100644 index 00000000000..1a78a92234c --- /dev/null +++ b/examples/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOut.md @@ -0,0 +1,10 @@ +# ExamplepbRepeatedResponseBodyOut + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Response** | [**[]ExamplepbRepeatedResponseBodyOutResponse**](examplepbRepeatedResponseBodyOutResponse.md) | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/examples/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOutResponse.md b/examples/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOutResponse.md new file mode 100644 index 00000000000..b11c5bb123f --- /dev/null +++ b/examples/clients/responsebody/docs/ExamplepbRepeatedResponseBodyOutResponse.md @@ -0,0 +1,10 @@ +# ExamplepbRepeatedResponseBodyOutResponse + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Data** | **string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/examples/clients/responsebody/docs/ExamplepbRepeatedResponseStrings.md b/examples/clients/responsebody/docs/ExamplepbRepeatedResponseStrings.md new file mode 100644 index 00000000000..702cfa03868 --- /dev/null +++ b/examples/clients/responsebody/docs/ExamplepbRepeatedResponseStrings.md @@ -0,0 +1,10 @@ +# ExamplepbRepeatedResponseStrings + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**Values** | **[]string** | | [optional] [default to null] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/examples/clients/responsebody/docs/ResponseBodyServiceApi.md b/examples/clients/responsebody/docs/ResponseBodyServiceApi.md index 7c4506cc991..458725faa9b 100644 --- a/examples/clients/responsebody/docs/ResponseBodyServiceApi.md +++ b/examples/clients/responsebody/docs/ResponseBodyServiceApi.md @@ -5,6 +5,8 @@ All URIs are relative to *http://localhost* Method | HTTP request | Description ------------- | ------------- | ------------- [**GetResponseBody**](ResponseBodyServiceApi.md#GetResponseBody) | **Get** /responsebody/{data} | +[**ListResponseBodies**](ResponseBodyServiceApi.md#ListResponseBodies) | **Get** /responsebodies/{data} | +[**ListResponseStrings**](ResponseBodyServiceApi.md#ListResponseStrings) | **Get** /responsestrings/{data} | # **GetResponseBody** @@ -34,3 +36,57 @@ No authorization required [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) +# **ListResponseBodies** +> []ExamplepbRepeatedResponseBodyOutResponse ListResponseBodies($data) + + + + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **data** | **string**| | + +### Return type + +[**[]ExamplepbRepeatedResponseBodyOutResponse**](examplepbRepeatedResponseBodyOutResponse.md) + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **ListResponseStrings** +> []string ListResponseStrings($data) + + + + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **data** | **string**| | + +### Return type + +**[]string** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/examples/clients/responsebody/examplepb_repeated_response_body_out.go b/examples/clients/responsebody/examplepb_repeated_response_body_out.go new file mode 100644 index 00000000000..18977ab5732 --- /dev/null +++ b/examples/clients/responsebody/examplepb_repeated_response_body_out.go @@ -0,0 +1,16 @@ +/* + * examples/proto/examplepb/response_body_service.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: version not set + * + * Generated by: https://github.com/swagger-api/swagger-codegen.git + */ + +package responsebody + +type ExamplepbRepeatedResponseBodyOut struct { + + Response []ExamplepbRepeatedResponseBodyOutResponse `json:"response,omitempty"` +} diff --git a/examples/clients/responsebody/examplepb_repeated_response_body_out_response.go b/examples/clients/responsebody/examplepb_repeated_response_body_out_response.go new file mode 100644 index 00000000000..c13c50cb507 --- /dev/null +++ b/examples/clients/responsebody/examplepb_repeated_response_body_out_response.go @@ -0,0 +1,16 @@ +/* + * examples/proto/examplepb/response_body_service.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: version not set + * + * Generated by: https://github.com/swagger-api/swagger-codegen.git + */ + +package responsebody + +type ExamplepbRepeatedResponseBodyOutResponse struct { + + Data string `json:"data,omitempty"` +} diff --git a/examples/clients/responsebody/examplepb_repeated_response_strings.go b/examples/clients/responsebody/examplepb_repeated_response_strings.go new file mode 100644 index 00000000000..94a35e877ab --- /dev/null +++ b/examples/clients/responsebody/examplepb_repeated_response_strings.go @@ -0,0 +1,16 @@ +/* + * examples/proto/examplepb/response_body_service.proto + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * OpenAPI spec version: version not set + * + * Generated by: https://github.com/swagger-api/swagger-codegen.git + */ + +package responsebody + +type ExamplepbRepeatedResponseStrings struct { + + Values []string `json:"values,omitempty"` +} diff --git a/examples/clients/responsebody/response_body_service_api.go b/examples/clients/responsebody/response_body_service_api.go index 15fa4e5bb63..000cecd6ddd 100644 --- a/examples/clients/responsebody/response_body_service_api.go +++ b/examples/clients/responsebody/response_body_service_api.go @@ -97,3 +97,123 @@ func (a ResponseBodyServiceApi) GetResponseBody(data string) (*ExamplepbResponse return successPayload, localVarAPIResponse, err } +/** + * + * + * @param data + * @return []ExamplepbRepeatedResponseBodyOutResponse + */ +func (a ResponseBodyServiceApi) ListResponseBodies(data string) ([]ExamplepbRepeatedResponseBodyOutResponse, *APIResponse, error) { + + var localVarHttpMethod = strings.ToUpper("Get") + // create path and map variables + localVarPath := a.Configuration.BasePath + "/responsebodies/{data}" + localVarPath = strings.Replace(localVarPath, "{"+"data"+"}", fmt.Sprintf("%v", data), -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([]ExamplepbRepeatedResponseBodyOutResponse) + 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: "ListResponseBodies", 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 data + * @return []string + */ +func (a ResponseBodyServiceApi) ListResponseStrings(data string) ([]string, *APIResponse, error) { + + var localVarHttpMethod = strings.ToUpper("Get") + // create path and map variables + localVarPath := a.Configuration.BasePath + "/responsestrings/{data}" + localVarPath = strings.Replace(localVarPath, "{"+"data"+"}", fmt.Sprintf("%v", data), -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([]string) + 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: "ListResponseStrings", 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/integration/integration_test.go b/examples/integration/integration_test.go index 96b36d04b58..f9ea66b9a26 100644 --- a/examples/integration/integration_test.go +++ b/examples/integration/integration_test.go @@ -1307,6 +1307,8 @@ func TestResponseBody(t *testing.T) { } testResponseBody(t, 8080) + testResponseBodies(t, 8080) + testResponseStrings(t, 8080) } func testResponseBody(t *testing.T, port int) { @@ -1332,3 +1334,51 @@ func testResponseBody(t *testing.T, port int) { t.Errorf("response = %q; want %q", got, want) } } + +func testResponseBodies(t *testing.T, port int) { + url := fmt.Sprintf("http://localhost:%d/responsebodies/foo", 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("ioutil.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) + } + + if got, want := string(buf), `[{"data":"foo"}]`; got != want { + t.Errorf("response = %q; want %q", got, want) + } +} + +func testResponseStrings(t *testing.T, port int) { + url := fmt.Sprintf("http://localhost:%d/responsestrings/foo", 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("ioutil.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) + } + + if got, want := string(buf), `["hello","foo"]`; got != want { + t.Errorf("response = %q; want %q", got, want) + } +} diff --git a/examples/proto/examplepb/response_body_service.pb.go b/examples/proto/examplepb/response_body_service.pb.go index fcad76e5898..922aafa8fef 100644 --- a/examples/proto/examplepb/response_body_service.pb.go +++ b/examples/proto/examplepb/response_body_service.pb.go @@ -35,7 +35,7 @@ func (m *ResponseBodyIn) Reset() { *m = ResponseBodyIn{} } func (m *ResponseBodyIn) String() string { return proto.CompactTextString(m) } func (*ResponseBodyIn) ProtoMessage() {} func (*ResponseBodyIn) Descriptor() ([]byte, []int) { - return fileDescriptor_response_body_service_5010ebf28bf7b965, []int{0} + return fileDescriptor_response_body_service_5b2acb9aa53489f0, []int{0} } func (m *ResponseBodyIn) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ResponseBodyIn.Unmarshal(m, b) @@ -73,7 +73,7 @@ func (m *ResponseBodyOut) Reset() { *m = ResponseBodyOut{} } func (m *ResponseBodyOut) String() string { return proto.CompactTextString(m) } func (*ResponseBodyOut) ProtoMessage() {} func (*ResponseBodyOut) Descriptor() ([]byte, []int) { - return fileDescriptor_response_body_service_5010ebf28bf7b965, []int{1} + return fileDescriptor_response_body_service_5b2acb9aa53489f0, []int{1} } func (m *ResponseBodyOut) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ResponseBodyOut.Unmarshal(m, b) @@ -111,7 +111,7 @@ func (m *ResponseBodyOut_Response) Reset() { *m = ResponseBodyOut_Respon func (m *ResponseBodyOut_Response) String() string { return proto.CompactTextString(m) } func (*ResponseBodyOut_Response) ProtoMessage() {} func (*ResponseBodyOut_Response) Descriptor() ([]byte, []int) { - return fileDescriptor_response_body_service_5010ebf28bf7b965, []int{1, 0} + return fileDescriptor_response_body_service_5b2acb9aa53489f0, []int{1, 0} } func (m *ResponseBodyOut_Response) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ResponseBodyOut_Response.Unmarshal(m, b) @@ -138,10 +138,127 @@ func (m *ResponseBodyOut_Response) GetData() string { return "" } +type RepeatedResponseBodyOut struct { + Response []*RepeatedResponseBodyOut_Response `protobuf:"bytes,2,rep,name=response,proto3" json:"response,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RepeatedResponseBodyOut) Reset() { *m = RepeatedResponseBodyOut{} } +func (m *RepeatedResponseBodyOut) String() string { return proto.CompactTextString(m) } +func (*RepeatedResponseBodyOut) ProtoMessage() {} +func (*RepeatedResponseBodyOut) Descriptor() ([]byte, []int) { + return fileDescriptor_response_body_service_5b2acb9aa53489f0, []int{2} +} +func (m *RepeatedResponseBodyOut) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RepeatedResponseBodyOut.Unmarshal(m, b) +} +func (m *RepeatedResponseBodyOut) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RepeatedResponseBodyOut.Marshal(b, m, deterministic) +} +func (dst *RepeatedResponseBodyOut) XXX_Merge(src proto.Message) { + xxx_messageInfo_RepeatedResponseBodyOut.Merge(dst, src) +} +func (m *RepeatedResponseBodyOut) XXX_Size() int { + return xxx_messageInfo_RepeatedResponseBodyOut.Size(m) +} +func (m *RepeatedResponseBodyOut) XXX_DiscardUnknown() { + xxx_messageInfo_RepeatedResponseBodyOut.DiscardUnknown(m) +} + +var xxx_messageInfo_RepeatedResponseBodyOut proto.InternalMessageInfo + +func (m *RepeatedResponseBodyOut) GetResponse() []*RepeatedResponseBodyOut_Response { + if m != nil { + return m.Response + } + return nil +} + +type RepeatedResponseBodyOut_Response struct { + Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RepeatedResponseBodyOut_Response) Reset() { *m = RepeatedResponseBodyOut_Response{} } +func (m *RepeatedResponseBodyOut_Response) String() string { return proto.CompactTextString(m) } +func (*RepeatedResponseBodyOut_Response) ProtoMessage() {} +func (*RepeatedResponseBodyOut_Response) Descriptor() ([]byte, []int) { + return fileDescriptor_response_body_service_5b2acb9aa53489f0, []int{2, 0} +} +func (m *RepeatedResponseBodyOut_Response) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RepeatedResponseBodyOut_Response.Unmarshal(m, b) +} +func (m *RepeatedResponseBodyOut_Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RepeatedResponseBodyOut_Response.Marshal(b, m, deterministic) +} +func (dst *RepeatedResponseBodyOut_Response) XXX_Merge(src proto.Message) { + xxx_messageInfo_RepeatedResponseBodyOut_Response.Merge(dst, src) +} +func (m *RepeatedResponseBodyOut_Response) XXX_Size() int { + return xxx_messageInfo_RepeatedResponseBodyOut_Response.Size(m) +} +func (m *RepeatedResponseBodyOut_Response) XXX_DiscardUnknown() { + xxx_messageInfo_RepeatedResponseBodyOut_Response.DiscardUnknown(m) +} + +var xxx_messageInfo_RepeatedResponseBodyOut_Response proto.InternalMessageInfo + +func (m *RepeatedResponseBodyOut_Response) GetData() string { + if m != nil { + return m.Data + } + return "" +} + +type RepeatedResponseStrings struct { + Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *RepeatedResponseStrings) Reset() { *m = RepeatedResponseStrings{} } +func (m *RepeatedResponseStrings) String() string { return proto.CompactTextString(m) } +func (*RepeatedResponseStrings) ProtoMessage() {} +func (*RepeatedResponseStrings) Descriptor() ([]byte, []int) { + return fileDescriptor_response_body_service_5b2acb9aa53489f0, []int{3} +} +func (m *RepeatedResponseStrings) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RepeatedResponseStrings.Unmarshal(m, b) +} +func (m *RepeatedResponseStrings) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RepeatedResponseStrings.Marshal(b, m, deterministic) +} +func (dst *RepeatedResponseStrings) XXX_Merge(src proto.Message) { + xxx_messageInfo_RepeatedResponseStrings.Merge(dst, src) +} +func (m *RepeatedResponseStrings) XXX_Size() int { + return xxx_messageInfo_RepeatedResponseStrings.Size(m) +} +func (m *RepeatedResponseStrings) XXX_DiscardUnknown() { + xxx_messageInfo_RepeatedResponseStrings.DiscardUnknown(m) +} + +var xxx_messageInfo_RepeatedResponseStrings proto.InternalMessageInfo + +func (m *RepeatedResponseStrings) GetValues() []string { + if m != nil { + return m.Values + } + return nil +} + func init() { proto.RegisterType((*ResponseBodyIn)(nil), "grpc.gateway.examples.examplepb.ResponseBodyIn") proto.RegisterType((*ResponseBodyOut)(nil), "grpc.gateway.examples.examplepb.ResponseBodyOut") proto.RegisterType((*ResponseBodyOut_Response)(nil), "grpc.gateway.examples.examplepb.ResponseBodyOut.Response") + proto.RegisterType((*RepeatedResponseBodyOut)(nil), "grpc.gateway.examples.examplepb.RepeatedResponseBodyOut") + proto.RegisterType((*RepeatedResponseBodyOut_Response)(nil), "grpc.gateway.examples.examplepb.RepeatedResponseBodyOut.Response") + proto.RegisterType((*RepeatedResponseStrings)(nil), "grpc.gateway.examples.examplepb.RepeatedResponseStrings") } // Reference imports to suppress errors if they are not otherwise used. @@ -157,6 +274,8 @@ const _ = grpc.SupportPackageIsVersion4 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type ResponseBodyServiceClient interface { GetResponseBody(ctx context.Context, in *ResponseBodyIn, opts ...grpc.CallOption) (*ResponseBodyOut, error) + ListResponseBodies(ctx context.Context, in *ResponseBodyIn, opts ...grpc.CallOption) (*RepeatedResponseBodyOut, error) + ListResponseStrings(ctx context.Context, in *ResponseBodyIn, opts ...grpc.CallOption) (*RepeatedResponseStrings, error) } type responseBodyServiceClient struct { @@ -176,9 +295,29 @@ func (c *responseBodyServiceClient) GetResponseBody(ctx context.Context, in *Res return out, nil } +func (c *responseBodyServiceClient) ListResponseBodies(ctx context.Context, in *ResponseBodyIn, opts ...grpc.CallOption) (*RepeatedResponseBodyOut, error) { + out := new(RepeatedResponseBodyOut) + err := c.cc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ResponseBodyService/ListResponseBodies", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *responseBodyServiceClient) ListResponseStrings(ctx context.Context, in *ResponseBodyIn, opts ...grpc.CallOption) (*RepeatedResponseStrings, error) { + out := new(RepeatedResponseStrings) + err := c.cc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ResponseBodyService/ListResponseStrings", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ResponseBodyServiceServer is the server API for ResponseBodyService service. type ResponseBodyServiceServer interface { GetResponseBody(context.Context, *ResponseBodyIn) (*ResponseBodyOut, error) + ListResponseBodies(context.Context, *ResponseBodyIn) (*RepeatedResponseBodyOut, error) + ListResponseStrings(context.Context, *ResponseBodyIn) (*RepeatedResponseStrings, error) } func RegisterResponseBodyServiceServer(s *grpc.Server, srv ResponseBodyServiceServer) { @@ -203,6 +342,42 @@ func _ResponseBodyService_GetResponseBody_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } +func _ResponseBodyService_ListResponseBodies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResponseBodyIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResponseBodyServiceServer).ListResponseBodies(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.gateway.examples.examplepb.ResponseBodyService/ListResponseBodies", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResponseBodyServiceServer).ListResponseBodies(ctx, req.(*ResponseBodyIn)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResponseBodyService_ListResponseStrings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ResponseBodyIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResponseBodyServiceServer).ListResponseStrings(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.gateway.examples.examplepb.ResponseBodyService/ListResponseStrings", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResponseBodyServiceServer).ListResponseStrings(ctx, req.(*ResponseBodyIn)) + } + return interceptor(ctx, in, info, handler) +} + var _ResponseBodyService_serviceDesc = grpc.ServiceDesc{ ServiceName: "grpc.gateway.examples.examplepb.ResponseBodyService", HandlerType: (*ResponseBodyServiceServer)(nil), @@ -211,32 +386,47 @@ var _ResponseBodyService_serviceDesc = grpc.ServiceDesc{ MethodName: "GetResponseBody", Handler: _ResponseBodyService_GetResponseBody_Handler, }, + { + MethodName: "ListResponseBodies", + Handler: _ResponseBodyService_ListResponseBodies_Handler, + }, + { + MethodName: "ListResponseStrings", + Handler: _ResponseBodyService_ListResponseStrings_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "examples/proto/examplepb/response_body_service.proto", } func init() { - proto.RegisterFile("examples/proto/examplepb/response_body_service.proto", fileDescriptor_response_body_service_5010ebf28bf7b965) -} - -var fileDescriptor_response_body_service_5010ebf28bf7b965 = []byte{ - // 257 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x49, 0xad, 0x48, 0xcc, - 0x2d, 0xc8, 0x49, 0x2d, 0xd6, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x87, 0x72, 0x0b, 0x92, 0xf4, - 0x8b, 0x52, 0x8b, 0x0b, 0xf2, 0xf3, 0x8a, 0x53, 0xe3, 0x93, 0xf2, 0x53, 0x2a, 0xe3, 0x8b, 0x53, - 0x8b, 0xca, 0x32, 0x93, 0x53, 0xf5, 0xc0, 0xaa, 0x84, 0xe4, 0xd3, 0x8b, 0x0a, 0x92, 0xf5, 0xd2, - 0x13, 0x4b, 0x52, 0xcb, 0x13, 0x2b, 0xf5, 0x60, 0x46, 0xe8, 0xc1, 0x35, 0x4b, 0xc9, 0xa4, 0xe7, - 0xe7, 0xa7, 0xe7, 0xa4, 0xea, 0x27, 0x16, 0x64, 0xea, 0x27, 0xe6, 0xe5, 0xe5, 0x97, 0x24, 0x96, - 0x64, 0xe6, 0xe7, 0x15, 0x43, 0xb4, 0x2b, 0xa9, 0x70, 0xf1, 0x05, 0x41, 0x4d, 0x77, 0xca, 0x4f, - 0xa9, 0xf4, 0xcc, 0x13, 0x12, 0xe2, 0x62, 0x49, 0x49, 0x2c, 0x49, 0x94, 0x60, 0x54, 0x60, 0xd4, - 0xe0, 0x0c, 0x02, 0xb3, 0x95, 0x3a, 0x18, 0xb9, 0xf8, 0x91, 0x95, 0xf9, 0x97, 0x96, 0x08, 0x85, - 0x72, 0x71, 0xc0, 0xdc, 0x25, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x6d, 0x64, 0xa9, 0x47, 0xc0, 0x2d, - 0x7a, 0x68, 0x66, 0xc0, 0xf9, 0x41, 0x70, 0xa3, 0xa4, 0xe4, 0xb8, 0x38, 0x60, 0xa2, 0xd8, 0x9c, - 0x62, 0xb4, 0x85, 0x91, 0x4b, 0x18, 0xd9, 0x98, 0x60, 0x48, 0x68, 0x08, 0xcd, 0x61, 0xe4, 0xe2, - 0x77, 0x4f, 0x2d, 0x41, 0x96, 0x12, 0xd2, 0x27, 0xc9, 0x41, 0x9e, 0x79, 0x52, 0x06, 0xa4, 0xfa, - 0x40, 0x49, 0xad, 0xe9, 0xf2, 0x93, 0xc9, 0x4c, 0x0a, 0x42, 0x22, 0xf0, 0x48, 0x02, 0xc5, 0x91, - 0x7e, 0x35, 0xc8, 0xad, 0xb5, 0x49, 0x70, 0x6f, 0x39, 0x71, 0x47, 0x71, 0xc2, 0x0d, 0x49, 0x62, - 0x03, 0x87, 0xbd, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xe8, 0x4a, 0x08, 0x5b, 0xf2, 0x01, 0x00, - 0x00, + proto.RegisterFile("examples/proto/examplepb/response_body_service.proto", fileDescriptor_response_body_service_5b2acb9aa53489f0) +} + +var fileDescriptor_response_body_service_5b2acb9aa53489f0 = []byte{ + // 378 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0xd3, 0xc1, 0x4a, 0xc3, 0x30, + 0x18, 0x07, 0x70, 0xb2, 0x8d, 0xb1, 0x65, 0xe0, 0x20, 0x93, 0x6d, 0x0c, 0xd1, 0x5a, 0x44, 0x7b, + 0x6a, 0x75, 0x7a, 0xd0, 0xa3, 0xbb, 0xc8, 0x40, 0x10, 0x3a, 0xbc, 0x08, 0x32, 0xd2, 0xf5, 0xa3, + 0x14, 0x66, 0x12, 0x9a, 0x6c, 0x3a, 0xc4, 0x8b, 0x27, 0x0f, 0xde, 0xbc, 0x78, 0xf0, 0x05, 0x7c, + 0x1e, 0x5f, 0xc1, 0x07, 0x91, 0x75, 0x6d, 0xa8, 0x73, 0xa0, 0xdb, 0xc1, 0x5b, 0xd3, 0x24, 0xff, + 0xfc, 0x9a, 0xef, 0x2b, 0x3e, 0x82, 0x3b, 0x7a, 0x23, 0x86, 0x20, 0x1d, 0x11, 0x71, 0xc5, 0x9d, + 0x64, 0x28, 0x3c, 0x27, 0x02, 0x29, 0x38, 0x93, 0xd0, 0xf7, 0xb8, 0x3f, 0xe9, 0x4b, 0x88, 0xc6, + 0xe1, 0x00, 0xec, 0x78, 0x15, 0xd9, 0x0a, 0x22, 0x31, 0xb0, 0x03, 0xaa, 0xe0, 0x96, 0x4e, 0xec, + 0x34, 0xc2, 0xd6, 0x9b, 0x5b, 0x1b, 0x01, 0xe7, 0xc1, 0x10, 0x1c, 0x2a, 0x42, 0x87, 0x32, 0xc6, + 0x15, 0x55, 0x21, 0x67, 0x72, 0xb6, 0xdd, 0xdc, 0xc1, 0x6b, 0x6e, 0x92, 0xde, 0xe1, 0xfe, 0xa4, + 0xcb, 0x08, 0xc1, 0x05, 0x9f, 0x2a, 0xda, 0x44, 0x06, 0xb2, 0xca, 0x6e, 0xfc, 0x6c, 0x3e, 0x21, + 0x5c, 0xcd, 0x2e, 0xbb, 0x18, 0x29, 0x72, 0x89, 0x4b, 0xa9, 0xab, 0x99, 0x33, 0x90, 0x55, 0x69, + 0x9f, 0xd8, 0xbf, 0x58, 0xec, 0xb9, 0x0c, 0x3d, 0x76, 0x75, 0x54, 0x6b, 0x13, 0x97, 0xd2, 0xb7, + 0x0b, 0x29, 0xaf, 0x08, 0x37, 0x5c, 0x10, 0x40, 0x15, 0xf8, 0xf3, 0xa4, 0xeb, 0x6f, 0xa4, 0xbc, + 0x55, 0x69, 0x9f, 0xfe, 0x81, 0xb4, 0x30, 0x6b, 0x15, 0xda, 0xc1, 0x4f, 0x59, 0x4f, 0x45, 0x21, + 0x0b, 0x24, 0xa9, 0xe3, 0xe2, 0x98, 0x0e, 0x47, 0x20, 0x9b, 0xc8, 0xc8, 0x5b, 0x65, 0x37, 0x19, + 0xb5, 0x9f, 0x0b, 0xb8, 0x96, 0x3d, 0xb9, 0x37, 0xab, 0x2d, 0x79, 0x43, 0xb8, 0x7a, 0x06, 0x2a, + 0x3b, 0x45, 0x9c, 0xa5, 0xae, 0xb7, 0xcb, 0x5a, 0xfb, 0xcb, 0xd6, 0xc3, 0xdc, 0x7d, 0xfc, 0xf8, + 0x7c, 0xc9, 0x19, 0x64, 0x5d, 0xb7, 0xdc, 0xb4, 0xe3, 0x9c, 0xfb, 0xe9, 0xe7, 0x3d, 0x78, 0xfa, + 0x26, 0xc8, 0x3b, 0xc2, 0xe4, 0x3c, 0x94, 0x59, 0x5f, 0x08, 0x72, 0x79, 0xe1, 0xf1, 0xaa, 0xe5, + 0x31, 0xad, 0x58, 0x6a, 0x92, 0x7a, 0x56, 0x1a, 0x82, 0x5c, 0x68, 0xad, 0x65, 0xad, 0x69, 0x49, + 0xfe, 0x01, 0x9b, 0x1c, 0x65, 0xee, 0xc5, 0xd8, 0x6d, 0xd2, 0xd0, 0x58, 0x39, 0x9b, 0x49, 0xb5, + 0x49, 0x3b, 0x74, 0x2a, 0x57, 0x65, 0x9d, 0xe6, 0x15, 0xe3, 0x3f, 0xf4, 0xf0, 0x2b, 0x00, 0x00, + 0xff, 0xff, 0x39, 0xa0, 0x6e, 0xed, 0x18, 0x04, 0x00, 0x00, } diff --git a/examples/proto/examplepb/response_body_service.pb.gw.go b/examples/proto/examplepb/response_body_service.pb.gw.go index 41480f44bb9..804a4815442 100644 --- a/examples/proto/examplepb/response_body_service.pb.gw.go +++ b/examples/proto/examplepb/response_body_service.pb.gw.go @@ -55,6 +55,60 @@ func request_ResponseBodyService_GetResponseBody_0(ctx context.Context, marshale } +func request_ResponseBodyService_ListResponseBodies_0(ctx context.Context, marshaler runtime.Marshaler, client ResponseBodyServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ResponseBodyIn + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["data"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "data") + } + + protoReq.Data, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "data", err) + } + + msg, err := client.ListResponseBodies(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func request_ResponseBodyService_ListResponseStrings_0(ctx context.Context, marshaler runtime.Marshaler, client ResponseBodyServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ResponseBodyIn + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["data"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "data") + } + + protoReq.Data, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "data", err) + } + + msg, err := client.ListResponseStrings(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + // RegisterResponseBodyServiceHandlerFromEndpoint is same as RegisterResponseBodyServiceHandler but // automatically dials to "endpoint" and closes the connection when "ctx" gets done. func RegisterResponseBodyServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { @@ -113,6 +167,46 @@ func RegisterResponseBodyServiceHandlerClient(ctx context.Context, mux *runtime. }) + mux.Handle("GET", pattern_ResponseBodyService_ListResponseBodies_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ResponseBodyService_ListResponseBodies_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResponseBodyService_ListResponseBodies_0(ctx, mux, outboundMarshaler, w, req, response_ResponseBodyService_ListResponseBodies_0{resp}, mux.GetForwardResponseOptions()...) + + }) + + mux.Handle("GET", pattern_ResponseBodyService_ListResponseStrings_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ResponseBodyService_ListResponseStrings_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResponseBodyService_ListResponseStrings_0(ctx, mux, outboundMarshaler, w, req, response_ResponseBodyService_ListResponseStrings_0{resp}, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -125,10 +219,36 @@ func (m response_ResponseBodyService_GetResponseBody_0) XXX_ResponseBody() inter return response.Response } +type response_ResponseBodyService_ListResponseBodies_0 struct { + proto.Message +} + +func (m response_ResponseBodyService_ListResponseBodies_0) XXX_ResponseBody() interface{} { + response := m.Message.(*RepeatedResponseBodyOut) + return response.Response +} + +type response_ResponseBodyService_ListResponseStrings_0 struct { + proto.Message +} + +func (m response_ResponseBodyService_ListResponseStrings_0) XXX_ResponseBody() interface{} { + response := m.Message.(*RepeatedResponseStrings) + return response.Values +} + var ( pattern_ResponseBodyService_GetResponseBody_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"responsebody", "data"}, "")) + + pattern_ResponseBodyService_ListResponseBodies_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"responsebodies", "data"}, "")) + + pattern_ResponseBodyService_ListResponseStrings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"responsestrings", "data"}, "")) ) var ( forward_ResponseBodyService_GetResponseBody_0 = runtime.ForwardResponseMessage + + forward_ResponseBodyService_ListResponseBodies_0 = runtime.ForwardResponseMessage + + forward_ResponseBodyService_ListResponseStrings_0 = runtime.ForwardResponseMessage ) diff --git a/examples/proto/examplepb/response_body_service.proto b/examples/proto/examplepb/response_body_service.proto index 65d430e348d..9e2f0165a3e 100644 --- a/examples/proto/examplepb/response_body_service.proto +++ b/examples/proto/examplepb/response_body_service.proto @@ -5,22 +5,39 @@ package grpc.gateway.examples.examplepb; import "google/api/annotations.proto"; -message ResponseBodyIn { - string data = 1; -} +message ResponseBodyIn { string data = 1; } message ResponseBodyOut { - message Response { - string data = 1; - } - Response response = 2; + message Response { string data = 1; } + Response response = 2; +} + +message RepeatedResponseBodyOut { + message Response { string data = 1; } + repeated Response response = 2; } +message RepeatedResponseStrings { repeated string values = 1; } + service ResponseBodyService { - rpc GetResponseBody(ResponseBodyIn) returns (ResponseBodyOut) { - option (google.api.http) = { - get: "/responsebody/{data}" - response_body: "response" - }; - } + rpc GetResponseBody(ResponseBodyIn) returns (ResponseBodyOut) { + option (google.api.http) = { + get : "/responsebody/{data}" + response_body : "response" + }; + } + + rpc ListResponseBodies(ResponseBodyIn) returns (RepeatedResponseBodyOut) { + option (google.api.http) = { + get : "/responsebodies/{data}" + response_body : "response" + }; + } + + rpc ListResponseStrings(ResponseBodyIn) returns (RepeatedResponseStrings) { + option (google.api.http) = { + get : "/responsestrings/{data}" + response_body : "values" + }; + } } diff --git a/examples/proto/examplepb/response_body_service.swagger.json b/examples/proto/examplepb/response_body_service.swagger.json index c13e7646bcf..c77d6ea05e0 100644 --- a/examples/proto/examplepb/response_body_service.swagger.json +++ b/examples/proto/examplepb/response_body_service.swagger.json @@ -15,6 +15,33 @@ "application/json" ], "paths": { + "/responsebodies/{data}": { + "get": { + "operationId": "ListResponseBodies", + "responses": { + "200": { + "description": "", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/examplepbRepeatedResponseBodyOutResponse" + } + } + } + }, + "parameters": [ + { + "name": "data", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "ResponseBodyService" + ] + } + }, "/responsebody/{data}": { "get": { "operationId": "GetResponseBody", @@ -38,9 +65,66 @@ "ResponseBodyService" ] } + }, + "/responsestrings/{data}": { + "get": { + "operationId": "ListResponseStrings", + "responses": { + "200": { + "description": "", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "parameters": [ + { + "name": "data", + "in": "path", + "required": true, + "type": "string" + } + ], + "tags": [ + "ResponseBodyService" + ] + } } }, "definitions": { + "examplepbRepeatedResponseBodyOut": { + "type": "object", + "properties": { + "response": { + "type": "array", + "items": { + "$ref": "#/definitions/examplepbRepeatedResponseBodyOutResponse" + } + } + } + }, + "examplepbRepeatedResponseBodyOutResponse": { + "type": "object", + "properties": { + "data": { + "type": "string" + } + } + }, + "examplepbRepeatedResponseStrings": { + "type": "object", + "properties": { + "values": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "examplepbResponseBodyOut": { "type": "object", "properties": { diff --git a/examples/server/responsebody.go b/examples/server/responsebody.go index 62cff2f47db..f27a0718be7 100644 --- a/examples/server/responsebody.go +++ b/examples/server/responsebody.go @@ -21,3 +21,19 @@ func (s *responseBodyServer) GetResponseBody(ctx context.Context, req *examples. }, }, nil } + +func (s *responseBodyServer) ListResponseBodies(ctx context.Context, req *examples.ResponseBodyIn) (*examples.RepeatedResponseBodyOut, error) { + return &examples.RepeatedResponseBodyOut{ + Response: []*examples.RepeatedResponseBodyOut_Response{ + &examples.RepeatedResponseBodyOut_Response{ + Data: req.Data, + }, + }, + }, nil +} + +func (s *responseBodyServer) ListResponseStrings(ctx context.Context, req *examples.ResponseBodyIn) (*examples.RepeatedResponseStrings, error) { + return &examples.RepeatedResponseStrings{ + Values: []string{"hello", req.Data}, + }, nil +} diff --git a/protoc-gen-grpc-gateway/BUILD.bazel b/protoc-gen-grpc-gateway/BUILD.bazel index 1af07f27f79..cb772efe9e1 100644 --- a/protoc-gen-grpc-gateway/BUILD.bazel +++ b/protoc-gen-grpc-gateway/BUILD.bazel @@ -25,7 +25,10 @@ go_binary( go_proto_compiler( name = "go_gen_grpc_gateway", - options = ["logtostderr=true"], + options = [ + "logtostderr=true", + "allow_repeated_fields_in_body=true", + ], plugin = ":protoc-gen-grpc-gateway", suffix = ".pb.gw.go", visibility = ["//visibility:public"], diff --git a/protoc-gen-swagger/defs.bzl b/protoc-gen-swagger/defs.bzl index a2933cdbeaa..d049ce596af 100644 --- a/protoc-gen-swagger/defs.bzl +++ b/protoc-gen-swagger/defs.bzl @@ -40,7 +40,7 @@ def _run_proto_gen_swagger(ctx, direct_proto_srcs, transitive_proto_srcs, action inputs = direct_proto_srcs + transitive_proto_srcs + [protoc_gen_swagger] - options = ["logtostderr=true"] + options = ["logtostderr=true", "allow_repeated_fields_in_body=true"] if grpc_api_configuration: options.append("grpc_api_configuration=%s" % grpc_api_configuration.path) inputs.append(grpc_api_configuration) diff --git a/protoc-gen-swagger/main.go b/protoc-gen-swagger/main.go index 5c921f6e163..174e4fad384 100644 --- a/protoc-gen-swagger/main.go +++ b/protoc-gen-swagger/main.go @@ -24,6 +24,7 @@ var ( useJSONNamesForFields = flag.Bool("json_names_for_fields", false, "if it sets Field.GetJsonName() will be used for generating swagger definitions, otherwise Field.GetName() will be used") repeatedPathParamSeparator = flag.String("repeated_path_param_separator", "csv", "configures how repeated fields should be split. Allowed values are `csv`, `pipes`, `ssv` and `tsv`.") versionFlag = flag.Bool("version", false, "print the current verison") + allowRepeatedFieldsInBody = flag.Bool("allow_repeated_fields_in_body", false, "allows to use repeated field in `body` and `response_body` field of `google.api.http` annotation option") ) // Variables set by goreleaser at build time @@ -72,6 +73,7 @@ func main() { reg.SetAllowMerge(*allowMerge) reg.SetMergeFileName(*mergeFileName) reg.SetUseJSONNamesForFields(*useJSONNamesForFields) + reg.SetAllowRepeatedFieldsInBody(*allowRepeatedFieldsInBody) if err := reg.SetRepeatedPathParamSeparator(*repeatedPathParamSeparator); err != nil { emitError(err) return @@ -154,6 +156,13 @@ func parseReqParam(param string, f *flag.FlagSet, pkgMap map[string]string) erro } continue } + if spec[0] == "allow_repeated_fields_in_body" { + err := f.Set(spec[0], "true") + if err != nil { + return fmt.Errorf("Cannot set flag %s: %v", p, err) + } + continue + } err := f.Set(spec[0], "") if err != nil { return fmt.Errorf("Cannot set flag %s: %v", p, err) diff --git a/protoc-gen-swagger/main_test.go b/protoc-gen-swagger/main_test.go index 7bdca480979..dc8e09bdb12 100644 --- a/protoc-gen-swagger/main_test.go +++ b/protoc-gen-swagger/main_test.go @@ -1,116 +1,129 @@ package main import ( + "errors" "flag" "reflect" "testing" ) func TestParseReqParam(t *testing.T) { - - f := flag.CommandLine - // this one must be first - with no leading clearFlags call it - // verifies our expectation of default values as we reset by - // clearFlags - pkgMap := make(map[string]string) - expected := map[string]string{} - err := parseReqParam("", f, pkgMap) - if err != nil { - t.Errorf("Test 0: unexpected parse error '%v'", err) + testcases := []struct { + name string + expected map[string]string + request string + expectedError error + allowDeleteBodyV bool + allowMergeV bool + allowRepeatedFieldsInBodyV bool + fileV string + importPathV string + mergeFileNameV string + }{ + { + // this one must be first - with no leading clearFlags call it + // verifies our expectation of default values as we reset by + // clearFlags + name: "Test 0", + expected: map[string]string{}, + request: "", + allowDeleteBodyV: false, allowMergeV: false, allowRepeatedFieldsInBodyV: false, + fileV: "-", importPathV: "", mergeFileNameV: "apidocs", + }, + { + name: "Test 1", + expected: map[string]string{"google/api/annotations.proto": "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"}, + request: "allow_delete_body,allow_merge,allow_repeated_fields_in_body,file=./foo.pb,import_prefix=/bar/baz,Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api", + allowDeleteBodyV: true, allowMergeV: true, allowRepeatedFieldsInBodyV: true, + fileV: "./foo.pb", importPathV: "/bar/baz", mergeFileNameV: "apidocs", + }, + { + name: "Test 2", + expected: map[string]string{"google/api/annotations.proto": "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"}, + request: "allow_delete_body=true,allow_merge=true,allow_repeated_fields_in_body=true,merge_file_name=test_name,file=./foo.pb,import_prefix=/bar/baz,Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api", + allowDeleteBodyV: true, allowMergeV: true, allowRepeatedFieldsInBodyV: true, + fileV: "./foo.pb", importPathV: "/bar/baz", mergeFileNameV: "test_name", + }, + { + name: "Test 3", + expected: map[string]string{"a/b/c.proto": "github.com/x/y/z", "f/g/h.proto": "github.com/1/2/3/"}, + request: "allow_delete_body=false,allow_merge=false,Ma/b/c.proto=github.com/x/y/z,Mf/g/h.proto=github.com/1/2/3/", + allowDeleteBodyV: false, allowMergeV: false, allowRepeatedFieldsInBodyV: false, + fileV: "stdin", importPathV: "", mergeFileNameV: "apidocs", + }, + { + name: "Test 4", + expected: map[string]string{}, + request: "", + allowDeleteBodyV: false, allowMergeV: false, allowRepeatedFieldsInBodyV: false, + fileV: "stdin", importPathV: "", mergeFileNameV: "apidocs", + }, + { + name: "Test 5", + expected: map[string]string{}, + request: "unknown_param=17", + expectedError: errors.New("Cannot set flag unknown_param=17: no such flag -unknown_param"), + allowDeleteBodyV: false, allowMergeV: false, allowRepeatedFieldsInBodyV: false, + fileV: "stdin", importPathV: "", mergeFileNameV: "apidocs", + }, + { + name: "Test 6", + expected: map[string]string{}, + request: "Mfoo", + expectedError: errors.New("Cannot set flag Mfoo: no such flag -Mfoo"), + allowDeleteBodyV: false, allowMergeV: false, allowRepeatedFieldsInBodyV: false, + fileV: "stdin", importPathV: "", mergeFileNameV: "apidocs", + }, + { + name: "Test 7", + expected: map[string]string{}, + request: "allow_delete_body,file,import_prefix,allow_merge,allow_repeated_fields_in_body,merge_file_name", + allowDeleteBodyV: true, allowMergeV: true, allowRepeatedFieldsInBodyV: true, + fileV: "", importPathV: "", mergeFileNameV: "", + }, + { + name: "Test 8", + expected: map[string]string{}, + request: "allow_delete_body,file,import_prefix,allow_merge,allow_repeated_fields_in_body=3,merge_file_name", + expectedError: errors.New(`Cannot set flag allow_repeated_fields_in_body=3: strconv.ParseBool: parsing "3": invalid syntax`), + allowDeleteBodyV: true, allowMergeV: true, allowRepeatedFieldsInBodyV: false, + fileV: "", importPathV: "", mergeFileNameV: "apidocs", + }, } - if !reflect.DeepEqual(pkgMap, expected) { - t.Errorf("Test 0: pkgMap parse error, expected '%v', got '%v'", expected, pkgMap) - } - checkFlags(false, false, "-", "", "apidocs", t, 0) - - clearFlags() - pkgMap = make(map[string]string) - expected = map[string]string{"google/api/annotations.proto": "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"} - err = parseReqParam("allow_delete_body,allow_merge,file=./foo.pb,import_prefix=/bar/baz,Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api", f, pkgMap) - if err != nil { - t.Errorf("Test 1: unexpected parse error '%v'", err) - } - if !reflect.DeepEqual(pkgMap, expected) { - t.Errorf("Test 1: pkgMap parse error, expected '%v', got '%v'", expected, pkgMap) - } - checkFlags(true, true, "./foo.pb", "/bar/baz", "apidocs", t, 1) - - clearFlags() - pkgMap = make(map[string]string) - expected = map[string]string{"google/api/annotations.proto": "github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api"} - err = parseReqParam("allow_delete_body=true,allow_merge=true,merge_file_name=test_name,file=./foo.pb,import_prefix=/bar/baz,Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api", f, pkgMap) - if err != nil { - t.Errorf("Test 2: unexpected parse error '%v'", err) - } - if !reflect.DeepEqual(pkgMap, expected) { - t.Errorf("Test 2: pkgMap parse error, expected '%v', got '%v'", expected, pkgMap) - } - checkFlags(true, true,"./foo.pb", "/bar/baz", "test_name", t, 2) - - clearFlags() - pkgMap = make(map[string]string) - expected = map[string]string{"a/b/c.proto": "github.com/x/y/z", "f/g/h.proto": "github.com/1/2/3/"} - err = parseReqParam("allow_delete_body=false,allow_merge=false,Ma/b/c.proto=github.com/x/y/z,Mf/g/h.proto=github.com/1/2/3/", f, pkgMap) - if err != nil { - t.Errorf("Test 3: unexpected parse error '%v'", err) - } - if !reflect.DeepEqual(pkgMap, expected) { - t.Errorf("Test 3: pkgMap parse error, expected '%v', got '%v'", expected, pkgMap) - } - checkFlags(false, false,"stdin", "", "apidocs", t, 3) - clearFlags() - pkgMap = make(map[string]string) - expected = map[string]string{} - err = parseReqParam("", f, pkgMap) - if err != nil { - t.Errorf("Test 4: unexpected parse error '%v'", err) - } - if !reflect.DeepEqual(pkgMap, expected) { - t.Errorf("Test 4: pkgMap parse error, expected '%v', got '%v'", expected, pkgMap) - } - checkFlags(false, false, "stdin", "", "apidocs", t, 4) - - clearFlags() - pkgMap = make(map[string]string) - expected = map[string]string{} - err = parseReqParam("unknown_param=17", f, pkgMap) - if err == nil { - t.Error("Test 5: expected parse error not returned") - } - if !reflect.DeepEqual(pkgMap, expected) { - t.Errorf("Test 5: pkgMap parse error, expected '%v', got '%v'", expected, pkgMap) - } - checkFlags(false, false,"stdin", "", "apidocs", t, 5) + for i, tc := range testcases { + t.Run(tc.name, func(tt *testing.T) { + f := flag.CommandLine + pkgMap := make(map[string]string) + err := parseReqParam(tc.request, f, pkgMap) + if tc.expectedError == nil { + if err != nil { + tt.Errorf("unexpected parse error '%v'", err) + } + if !reflect.DeepEqual(pkgMap, tc.expected) { + tt.Errorf("pkgMap parse error, expected '%v', got '%v'", tc.expected, pkgMap) + } + } else { + if err == nil { + tt.Error("expected parse error not returned") + } + if !reflect.DeepEqual(pkgMap, tc.expected) { + tt.Errorf("pkgMap parse error, expected '%v', got '%v'", tc.expected, pkgMap) + } + if err.Error() != tc.expectedError.Error() { + tt.Errorf("expected error malformed, expected %q, go %q", tc.expectedError.Error(), err.Error()) + } + } + checkFlags(tc.allowDeleteBodyV, tc.allowMergeV, tc.allowRepeatedFieldsInBodyV, tc.fileV, tc.importPathV, tc.mergeFileNameV, tt, i) - clearFlags() - pkgMap = make(map[string]string) - expected = map[string]string{} - err = parseReqParam("Mfoo", f, pkgMap) - if err == nil { - t.Error("Test 6: expected parse error not returned") + clearFlags() + }) } - if !reflect.DeepEqual(pkgMap, expected) { - t.Errorf("Test 6: pkgMap parse error, expected '%v', got '%v'", expected, pkgMap) - } - checkFlags(false, false,"stdin", "", "apidocs", t, 6) - - clearFlags() - pkgMap = make(map[string]string) - expected = map[string]string{} - err = parseReqParam("allow_delete_body,file,import_prefix,allow_merge,merge_file_name", f, pkgMap) - if err != nil { - t.Errorf("Test 7: unexpected parse error '%v'", err) - } - if !reflect.DeepEqual(pkgMap, expected) { - t.Errorf("Test 7: pkgMap parse error, expected '%v', got '%v'", expected, pkgMap) - } - checkFlags(true, true, "", "", "", t, 7) } -func checkFlags(allowDeleteV, allowMergeV bool, fileV, importPathV, mergeFileNameV string, t *testing.T, tid int) { +func checkFlags(allowDeleteV, allowMergeV, allowRepeatedFieldsInBodyV bool, fileV, importPathV, mergeFileNameV string, t *testing.T, tid int) { if *importPrefix != importPathV { t.Errorf("Test %v: import_prefix misparsed, expected '%v', got '%v'", tid, importPathV, *importPrefix) } @@ -126,6 +139,9 @@ func checkFlags(allowDeleteV, allowMergeV bool, fileV, importPathV, mergeFileNam if *mergeFileName != mergeFileNameV { t.Errorf("Test %v: merge_file_name misparsed, expected '%v', got '%v'", tid, mergeFileNameV, *mergeFileName) } + if *allowRepeatedFieldsInBody != allowRepeatedFieldsInBodyV { + t.Errorf("Test %v: allow_repeated_fields_in_body misparsed, expected '%v', got '%v'", tid, allowRepeatedFieldsInBodyV, *allowRepeatedFieldsInBody) + } } func clearFlags() { @@ -133,5 +149,6 @@ func clearFlags() { *file = "stdin" *allowDeleteBody = false *allowMerge = false + *allowRepeatedFieldsInBody = false *mergeFileName = "apidocs" }