Skip to content

marshal_jsonpb: add check for slice sub types implementing proto.Message #856

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

Merged
merged 2 commits into from
Jan 21, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions examples/clients/responsebody/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ go_library(
"examplepb_response_body_out_response.go",
"examplepb_response_body_req.go",
"response_body_service_api.go",
"response_response_type.go",
],
importpath = "github.com/grpc-ecosystem/grpc-gateway/examples/clients/responsebody",
visibility = ["//visibility:public"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**Data** | **string** | | [optional] [default to null]
**Type_** | [**ResponseResponseType**](ResponseResponseType.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)

Expand Down
9 changes: 9 additions & 0 deletions examples/clients/responsebody/docs/ResponseResponseType.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# ResponseResponseType

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ package responsebody
type ExamplepbRepeatedResponseBodyOutResponse struct {

Data string `json:"data,omitempty"`

Type_ ResponseResponseType `json:"type,omitempty"`
}
14 changes: 14 additions & 0 deletions examples/clients/responsebody/response_response_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 ResponseResponseType struct {
}
7 changes: 6 additions & 1 deletion examples/integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1365,7 +1365,7 @@ func testResponseStrings(t *testing.T, port int) {
// Run Secondary server with different marshalling
ch := make(chan error)
go func() {
if err := runGateway(ctx, ":8081", runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{EmitDefaults: true})); err != nil {
if err := runGateway(ctx, ":8081", runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{EnumsAsInts: false, EmitDefaults: true})); err != nil {
ch <- fmt.Errorf("cannot run gateway service: %v", err)
}
}()
Expand All @@ -1387,6 +1387,11 @@ func testResponseStrings(t *testing.T, port int) {
expectedCode: http.StatusOK,
expectedBody: `[]`,
},
{
endpoint: fmt.Sprintf("http://localhost:%d/responsebodies/foo", port),
expectedCode: http.StatusOK,
expectedBody: `[{"data":"foo","type":"UNKNOWN"}]`,
},
} {
t.Run(strconv.Itoa(i), func(t *testing.T) {
url := spec.endpoint
Expand Down
120 changes: 81 additions & 39 deletions examples/proto/examplepb/response_body_service.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion examples/proto/examplepb/response_body_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@ message ResponseBodyOut {
}

message RepeatedResponseBodyOut {
message Response { string data = 1; }
message Response {
string data = 1;
enum ResponseType {
// UNKNOWN
UNKNOWN = 0;
// A is 1
A = 1;
// B is 2
B = 2;
}
ResponseType type = 3;
}
repeated Response response = 2;
}

Expand Down
13 changes: 13 additions & 0 deletions examples/proto/examplepb/response_body_service.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,16 @@
}
},
"definitions": {
"ResponseResponseType": {
"type": "string",
"enum": [
"UNKNOWN",
"A",
"B"
],
"default": "UNKNOWN",
"title": "- UNKNOWN: UNKNOWN\n - A: A is 1\n - B: B is 2"
},
"examplepbRepeatedResponseBodyOut": {
"type": "object",
"properties": {
Expand All @@ -111,6 +121,9 @@
"properties": {
"data": {
"type": "string"
},
"type": {
"$ref": "#/definitions/ResponseResponseType"
}
}
},
Expand Down
39 changes: 37 additions & 2 deletions runtime/marshal_jsonpb.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ func (j *JSONPb) marshalTo(w io.Writer, v interface{}) error {
return (*jsonpb.Marshaler)(j).Marshal(w, p)
}

var (
// protoMessageType is stored to prevent constant lookup of the same type at runtime.
protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
)

// marshalNonProto marshals a non-message field of a protobuf message.
// This function does not correctly marshals arbitrary data structure into JSON,
// but it is only capable of marshaling non-message field values of protobuf,
Expand All @@ -67,8 +72,38 @@ func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) {
rv = rv.Elem()
}

if rv.Kind() == reflect.Slice && rv.IsNil() && j.EmitDefaults {
return []byte("[]"), nil
if rv.Kind() == reflect.Slice {
if rv.IsNil() {
if j.EmitDefaults {
return []byte("[]"), nil
}
return []byte("null"), nil
}

if rv.Type().Elem().Implements(protoMessageType) {
var buf bytes.Buffer
err := buf.WriteByte('[')
if err != nil {
return nil, err
}
for i := 0; i < rv.Len(); i++ {
if i != 0 {
err = buf.WriteByte(',')
if err != nil {
return nil, err
}
}
if err = (*jsonpb.Marshaler)(j).Marshal(&buf, rv.Index(i).Interface().(proto.Message)); err != nil {
return nil, err
}
}
err = buf.WriteByte(']')
if err != nil {
return nil, err
}

return buf.Bytes(), nil
}
}

if rv.Kind() == reflect.Map {
Expand Down
Loading