grpc-gateway icon indicating copy to clipboard operation
grpc-gateway copied to clipboard

Unable to specify empty response schema

Open aleert opened this issue 2 years ago • 12 comments

🐛 Bug Report

Response schema states that

// `Schema` optionally defines the structure of the response.
  // If `Schema` is not provided, it means there is no content to the response.
  Schema schema = 2;

however when i omit schema from rpc response defenition it still renders as "schema": {} in api.swagger.json which in editor rendered as Screenshot 2022-09-12 at 13 53 36

To Reproduce

Define RPC with empty schema response option

 option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
      responses: {
        key: "202"
        value: {
          description: "test description",
        },
      };
  }

Expected behavior

schema key not renders in api.swagger.json and rendered schema looks like Screenshot 2022-09-12 at 14 06 16

aleert avatar Sep 12 '22 11:09 aleert

Have you tried using google.protobuf.Empty as the response type?

johanbrandhorst avatar Sep 14 '22 02:09 johanbrandhorst

Have you tried using google.protobuf.Empty as the response type?

Yes, it displays empty message as example response value and has description according to google.protobuf.Empty docs as schema key in api.swagger.json is still generated.

Screenshot 2022-09-14 at 12 37 46

aleert avatar Sep 14 '22 09:09 aleert

I had a look at the spec, and I agree that it should be possible to set this to something empty given this definition of the field: image

However, with the way that we specify responses using the annotations, how would we tell the difference between an empty response and a response that's not been set at all? The message definition is here: https://buf.build/grpc-ecosystem/grpc-gateway/file/main:protoc-gen-openapiv2/options/openapiv2.proto#L230. Setting this to null is the same as not setting it at all, and we currently only let this overwrite the default response value (inferred from the type of the response) if it's set, so I don't see how we could possible support this given our existing annotations. Have you got any ideas?

johanbrandhorst avatar Sep 17 '22 03:09 johanbrandhorst

Hey, Pitching in.

BTW, buf links are not permanent (https://buf.build/grpc-ecosystem/grpc-gateway/file/main:protoc-gen-openapiv2/options/openapiv2.proto#L230)

But if we meant this: https://github.com/grpc-ecosystem/grpc-gateway/blob/67896a82850755d6f6ddfa68e034d1e5b7d7512b/protoc-gen-openapiv2/options/openapiv2.proto#L280

Can't we change Schema schema = 2 to optional Schema schema = 2?

However I understand that (https://google.aip.dev/180#moving-into-oneofs):

Existing fields must not be moved into or out of a oneof. This is a backwards-incompatible change in the Go protobuf stubs.

And optional=oneof

same-id avatar Jan 23 '23 09:01 same-id

I'd be worried about making a change like that to a type that's so central to all our annotations, and I'm not convinced it would make a difference either. If we can already set it to null today, what is optional going to add? But feel free to experiment with it 😄.

johanbrandhorst avatar Jan 23 '23 17:01 johanbrandhorst

In this case, this is a breaking change, but however I think it's fine since I guess no one is using those annotations outside of grpc-gateway Go generated files - so I guess that the blast radius is confined.

To be honest, this example is a microcosmos of actual grpc trancoding challanges (since we want our annotation protobuf to have the same structure as the schema json): Since in json files you can specify the absence of the field as another state, but protobuf tooling auto generates empty classes/structs in all languages even if they are missing from the wire.

Back to the task at hand, How do I make sure that there is no response generated at all, at least for documentation purposes (even if API does return {})

same-id avatar Jan 23 '23 21:01 same-id

Would disable_default_responses be useful? https://github.com/grpc-ecosystem/grpc-gateway/blob/main/protoc-gen-openapiv2/main.go#L44

johanbrandhorst avatar Jan 23 '23 22:01 johanbrandhorst

Actually this is going too strong in the other direction. Default 200 responses are not auto generated for the 99 out of the 100 API's which are not google.protobuf.Empty (One would say that I got 99 problems and google.protobuf.Empty ain't one)

I think there are two ways to solve the issue in a more reasonable way:

  1. Add optional on the Schema field. This is a breaking change but probably no one uses generated annotation code other than grpc-gateway itself. If we don't like it, we can add "yet-another-feature-flag" or add a manual "has_schema" to the protobuf definition.
  2. We can treat google.protobuf.Empty specially and not specify a returned schema. However if we just return a non myorg.protobuf.Empty it will still show {} as the response.

To be honest I'm also not so sure about if it's okay to document that an API call does not return content, when in practice it does, this is a limitation of gRPC transcoding.

We can feature-flag option 2 under omit_google_protobuf_empty_responses or something. WDYT?

same-id avatar Jan 24 '23 08:01 same-id

I think we already have a special case for google.protobuf.Empty, do we not? I wouldn't necessarily be opposed to expanding that if it's not already accurate enough. What does the return value associated with a google.protobuf.Empty look like today?

johanbrandhorst avatar Jan 24 '23 17:01 johanbrandhorst

image

Same as just message AnotherEmpty {}

same-id avatar Jan 24 '23 18:01 same-id

I guess that's actually accurate per the behavior of the JSON marshaler, so maybe there's not much we can do there.

johanbrandhorst avatar Jan 25 '23 17:01 johanbrandhorst

Yea

To be honest I'm also not so sure about if it's okay to document that an API call does not return content, when in practice it does, this is a limitation of gRPC transcoding.

It's just for optics mainly

same-id avatar Jan 29 '23 19:01 same-id