diff --git a/examples/integration/integration_test.go b/examples/integration/integration_test.go index f9ea66b9a26..c361300371d 100644 --- a/examples/integration/integration_test.go +++ b/examples/integration/integration_test.go @@ -1360,25 +1360,57 @@ func testResponseBodies(t *testing.T, port int) { } 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 - } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // 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 { + ch <- fmt.Errorf("cannot run gateway service: %v", err) + } + }() - if got, want := resp.StatusCode, http.StatusOK; got != want { - t.Errorf("resp.StatusCode = %d; want %d", got, want) - t.Logf("%s", buf) - } + port = 8081 - if got, want := string(buf), `["hello","foo"]`; got != want { - t.Errorf("response = %q; want %q", got, want) + for i, spec := range []struct { + endpoint string + expectedCode int + expectedBody string + }{ + { + endpoint: fmt.Sprintf("http://localhost:%d/responsestrings/foo", port), + expectedCode: http.StatusOK, + expectedBody: `["hello","foo"]`, + }, + { + endpoint: fmt.Sprintf("http://localhost:%d/responsestrings/empty", port), + expectedCode: http.StatusOK, + expectedBody: `[]`, + }, + } { + t.Run(strconv.Itoa(i), func(t *testing.T) { + url := spec.endpoint + 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, spec.expectedCode; got != want { + t.Errorf("resp.StatusCode = %d; want %d", got, want) + t.Logf("%s", buf) + } + + if got, want := string(buf), spec.expectedBody; got != want { + t.Errorf("response = %q; want %q", got, want) + } + }) } + } diff --git a/examples/server/responsebody.go b/examples/server/responsebody.go index f27a0718be7..e5dc800a423 100644 --- a/examples/server/responsebody.go +++ b/examples/server/responsebody.go @@ -33,6 +33,11 @@ func (s *responseBodyServer) ListResponseBodies(ctx context.Context, req *exampl } func (s *responseBodyServer) ListResponseStrings(ctx context.Context, req *examples.ResponseBodyIn) (*examples.RepeatedResponseStrings, error) { + if req.Data == "empty" { + return &examples.RepeatedResponseStrings{ + Values: []string{}, + }, nil + } return &examples.RepeatedResponseStrings{ Values: []string{"hello", req.Data}, }, nil diff --git a/runtime/marshal_jsonpb.go b/runtime/marshal_jsonpb.go index f56072a6f15..1567944e6f2 100644 --- a/runtime/marshal_jsonpb.go +++ b/runtime/marshal_jsonpb.go @@ -67,6 +67,10 @@ 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.Map { m := make(map[string]*json.RawMessage) for _, k := range rv.MapKeys() { diff --git a/runtime/marshal_jsonpb_test.go b/runtime/marshal_jsonpb_test.go index 00dcfd99c5e..72d791d262b 100644 --- a/runtime/marshal_jsonpb_test.go +++ b/runtime/marshal_jsonpb_test.go @@ -3,6 +3,7 @@ package runtime_test import ( "bytes" "reflect" + "strconv" "strings" "testing" @@ -614,3 +615,132 @@ var ( // TODO(yugui) Add other well-known types once jsonpb supports them } ) + +func TestJSONPbMarshalResponseBodies(t *testing.T) { + for i, spec := range []struct { + input interface{} + emitDefaults bool + verifier func(json string) + }{ + { + input: &examplepb.ResponseBodyOut{ + Response: &examplepb.ResponseBodyOut_Response{Data: "abcdef"}, + }, + verifier: func(json string) { + expected := `{"response":{"data":"abcdef"}}` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + emitDefaults: true, + input: &examplepb.ResponseBodyOut{}, + verifier: func(json string) { + expected := `{"response":null}` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + input: &examplepb.RepeatedResponseBodyOut_Response{}, + verifier: func(json string) { + expected := `{}` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + emitDefaults: true, + input: &examplepb.RepeatedResponseBodyOut_Response{}, + verifier: func(json string) { + expected := `{"data":""}` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + input: ([]*examplepb.RepeatedResponseBodyOut_Response)(nil), + verifier: func(json string) { + expected := `null` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + emitDefaults: true, + input: ([]*examplepb.RepeatedResponseBodyOut_Response)(nil), + verifier: func(json string) { + expected := `[]` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + input: []*examplepb.RepeatedResponseBodyOut_Response{}, + verifier: func(json string) { + expected := `[]` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + input: []string{"something"}, + verifier: func(json string) { + expected := `["something"]` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + input: []string{}, + verifier: func(json string) { + expected := `[]` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + input: ([]string)(nil), + verifier: func(json string) { + expected := `null` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + { + emitDefaults: true, + input: ([]string)(nil), + verifier: func(json string) { + expected := `[]` + if json != expected { + t.Errorf("json not equal (%q, %q)", json, expected) + } + }, + }, + } { + + t.Run(strconv.Itoa(i), func(t *testing.T) { + m := runtime.JSONPb{ + EmitDefaults: spec.emitDefaults, + } + val := spec.input + buf, err := m.Marshal(val) + if err != nil { + t.Errorf("m.Marshal(%v) failed with %v; want success; spec=%v", val, err, spec) + } + if spec.verifier != nil { + spec.verifier(string(buf)) + } + }) + } +}