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

Support emitting default values in JSON #233

Closed
philipithomas opened this issue Oct 12, 2016 · 17 comments
Closed

Support emitting default values in JSON #233

philipithomas opened this issue Oct 12, 2016 · 17 comments
Labels
breaking change Will require a major version increment help wanted
Milestone

Comments

@philipithomas
Copy link
Contributor

Right now, github.com/golang/protobuf hardcodes omitempty, and the gateway does not support a way to enable the EmitDefaults option in

This produces unintended behavior - like all false booleans or zero integers being omitted from JSON apis. I don't think it's wise to assume that API consumers know that the absence of a response parameter means that it is a specific empty value.

I'm opening this issue for tracking - I'm not sure what the best solution is at this time though.

@philipithomas
Copy link
Contributor Author

I was able to implement this using the WithMarshalerOption like this:

gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{OrigName: true, EmitDefaults: true}))

I think this behavior is a hurdle and unexpected behavior for new users of this system.

I propose one of two changes:

  1. Explicit documentation in the README of this behavior, including documentation for how to override it

  2. Changing the default marshaler to include EmitDefaults.

What is opinion here?

@achew22
Copy link
Collaborator

achew22 commented Oct 13, 2016

I think I'm comfortable with your first suggested solution. Let's not do anything without getting thoughts from @yugui. What do you think?

@philipithomas
Copy link
Contributor Author

@yugui Wanted to follow up on this - what are your thoughts?

@yugui
Copy link
Member

yugui commented Oct 19, 2016

Sorry for my late reply.
Although it breaks compatibility, I still prefer the option (2) because it is more commonly used in REST APIs and we still keep a way to disable EmitDefaults.
I'm happy to review such a PR.

@jinleileiking
Copy link

Not Merged?

@jinleileiking
Copy link

@philipithomas

gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{OrigName: true, EmitDefaults: true}))

very, very useful!

@achew22
Copy link
Collaborator

achew22 commented Dec 14, 2017

This is now fixed

@achew22 achew22 closed this as completed Dec 14, 2017
@dhrp
Copy link

dhrp commented Dec 16, 2017

@achew22, @philipithomas I believe somehow @achew22 's commit for EmitEmpty has dropped out.

I was trying it and it doesn't work. I you look at the code changes in https://github.com/grpc-ecosystem/grpc-gateway/pull/145/files you'll notice there is no mention of 'Emit' anywhere. So perhaps it got lost rebasing or something?

@ornithocoder
Copy link

Hi there! Any progress on this one? I see the issue closed but #242 isn't merged yet. My team would love to see this one merged to simplify our interfaces and reduce boilerplate code on clients. Thank you!

@achew22
Copy link
Collaborator

achew22 commented Dec 22, 2017

Ah, I see what happened here. I was mistaken to close this -- my bad. Unfortunately there hasn't been progress.

@ornithocoder, if you were to extend the work and fix the tests I would be more than happy to merge it as soon as it goes green. It's just a little bit of test cleanup required.

@ornithocoder
Copy link

Hi @philipithomas / @achew22, here's part of the patch for the tests. It doesn't fix all the tests, tho. client_test.go and integration_test.go still have to be fixed.

diff --git a/examples/browser/a_bit_of_everything_service.spec.js b/examples/browser/a_bit_of_everything_service.spec.js
index edcbebe..f99c867 100644
--- a/examples/browser/a_bit_of_everything_service.spec.js
+++ b/examples/browser/a_bit_of_everything_service.spec.js
@@ -34,6 +34,15 @@ describe('ABitOfEverythingService', function() {
       sint32_value: 2147483647,
       sint64_value: "4611686018427387903",
       nonConventionalNameValue: "camelCase",
+      single_nested: null,
+      nested: [  ],
+      enum_value: "ZERO",
+      repeated_string_value: [  ],
+      map_value: Object({  }),
+      mapped_string_value: Object({  }),
+      mapped_nested_value: Object({  }),
+      timestamp_value: null,
+      repeated_enum_value: [  ],
     };
 
     beforeEach(function(done) {
@@ -72,10 +81,9 @@ describe('ABitOfEverythingService', function() {
       sint32_value: 2147483647,
       sint64_value: "4611686018427387903",
       nonConventionalNameValue: "camelCase",
-
       nested: [
-       { name: "bar", amount: 10 },
-       { name: "baz", amount: 20 },
+       { name: "bar", amount: 10, ok: 'FALSE' },
+       { name: "baz", amount: 20, ok: 'FALSE' },
       ],
       repeated_string_value: ["a", "b", "c"],
       oneof_string: "x",
@@ -83,9 +91,13 @@ describe('ABitOfEverythingService', function() {
       map_value: { a: 1, b: 2 },
       mapped_string_value: { a: "x", b: "y" },
       mapped_nested_value: {
-        a: { name: "x", amount: 1 },
-        b: { name: "y", amount: 2 },
+        a: { name: "x", amount: 1, ok: 'FALSE' },
+        b: { name: "y", amount: 2, ok: 'FALSE' },
       },
+      single_nested: null,
+      enum_value: "ZERO",
+      timestamp_value: null,
+      repeated_enum_value: [  ],
     };
 
     beforeEach(function(done) {
diff --git a/examples/browser/echo_service.spec.js b/examples/browser/echo_service.spec.js
index 97888c3..eca49f9 100644
--- a/examples/browser/echo_service.spec.js
+++ b/examples/browser/echo_service.spec.js
@@ -21,7 +21,7 @@ describe('EchoService', function() {
           {id: "foo"},
           {responseContentType: "application/json"}
       ).then(function(resp) {
-        expect(resp.obj).toEqual({id: "foo"});
+        expect(resp.obj).toEqual({id: "foo", num: '0'});
       }).catch(function(err) {
         done.fail(err);
       }).then(done);
@@ -34,7 +34,7 @@ describe('EchoService', function() {
           {body: {id: "foo"}},
           {responseContentType: "application/json"}
       ).then(function(resp) {
-        expect(resp.obj).toEqual({id: "foo"});
+        expect(resp.obj).toEqual({id: "foo", num: '0'});
       }).catch(function(err) {
         done.fail(err);
       }).then(done);

achew22 added a commit to achew22/grpc-gateway that referenced this issue Feb 10, 2018
One of the recurring themes of this project has been trouble around the
default marshaller. This change modifies it to be more what people
expect when they first start the project.

1.  It emits the proto3 json style version of field names instead of the
    field name as it appeared in the .proto file.
2.  It emits zero values for fields. This means that if you have a field
    that is unset it will now have a value unlike before.

Fixes: grpc-ecosystem#540, grpc-ecosystem#375, grpc-ecosystem#254, grpc-ecosystem#233
achew22 added a commit to achew22/grpc-gateway that referenced this issue Apr 9, 2018
One of the recurring themes of this project has been trouble around the
default marshaller. This change modifies it to be more what people
expect when they first start the project.

1.  It emits the proto3 json style version of field names instead of the
    field name as it appeared in the .proto file.
2.  It emits zero values for fields. This means that if you have a field
    that is unset it will now have a value unlike before.

Upgrade to swagger-codegen 2.4.0

Also fix a regex-o in .travis.yml. + needed to be escaped.

Fixes: grpc-ecosystem#540, grpc-ecosystem#375, grpc-ecosystem#254, grpc-ecosystem#233
flw-cn pushed a commit to flw-cn/grpc-gateway that referenced this issue Apr 28, 2018
In order to make APIs more restful, require the marshaler to emit
default values. By default, it omits empty types. This causes
unexpected REST behavior by omitting all false values or zero-valued
integers.

Closes grpc-ecosystem#233
flw-cn pushed a commit to flw-cn/grpc-gateway that referenced this issue Apr 28, 2018
In order to make APIs more restful, require the marshaler to emit
default values. By default, it omits empty types. This causes
unexpected REST behavior by omitting all false values or zero-valued
integers.

Closes grpc-ecosystem#233
flw-cn pushed a commit to flw-cn/grpc-gateway that referenced this issue Apr 28, 2018
In order to make APIs more restful, require the marshaler to emit
default values. By default, it omits empty types. This causes
unexpected REST behavior by omitting all false values or zero-valued
integers.

Closes grpc-ecosystem#233
flw-cn pushed a commit to flw-cn/grpc-gateway that referenced this issue Apr 28, 2018
In order to make APIs more restful, require the marshaler to emit
default values. By default, it omits empty types. This causes
unexpected REST behavior by omitting all false values or zero-valued
integers.

Closes grpc-ecosystem#233
flw-cn pushed a commit to flw-cn/grpc-gateway that referenced this issue Apr 28, 2018
In order to make APIs more restful, require the marshaler to emit
default values. By default, it omits empty types. This causes
unexpected REST behavior by omitting all false values or zero-valued
integers.

If you like to override its behavior back, use the WithMarshalerOption:

```go
gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{OrigName: true, EmitDefaults: false}))
```

Closes grpc-ecosystem#233
flw-cn pushed a commit to flw-cn/grpc-gateway that referenced this issue Apr 30, 2018
In order to make APIs more restful, require the marshaler to emit
default values. By default, it omits empty types. This causes
unexpected REST behavior by omitting all false values or zero-valued
integers.

If you like to override its behavior back, use the WithMarshalerOption:

```go
gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{OrigName: true, EmitDefaults: false}))
```

Closes grpc-ecosystem#233
@johanbrandhorst
Copy link
Collaborator

This is part of the v2 work in #546.

@stale
Copy link

stale bot commented Sep 9, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@johanbrandhorst
Copy link
Collaborator

We want to support this in v2

@johanbrandhorst
Copy link
Collaborator

This was merged with #1377

@seanlaff
Copy link
Contributor

Would it make sense to mark all fields in the generated swagger required: true when this flag is enabled? As of now the swagger shows every field in a response as optional, which means any code (eg typescript) generated from the swagger requires null-checking fields that can't ever be null.

Seems like it may be challenging to do given people use the same message in both request/response, in which case the swagger generator would need to generate 2 schemas (unless there is a way for an Operation to say its response fields are all required, but I am doubtful that exists).

@johanbrandhorst
Copy link
Collaborator

In proto3 all fields are required by default. Nullability is explicit using the optional keyword. I don't think the semantics translate cleanly to OpenAPI unfortunately (we've gone back and forth on this) so I don't want to make any behavioural changes at this point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking change Will require a major version increment help wanted
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants