-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
FR: Promote a field in the returned JSON message to a top-level returned value #707
Comments
In my experience, I've deeply regreted not returning an object at the top level. It made migrating that endpoint a horribly breaking change and required months and months of work to get things back in shape. That said, if you're trying to reverse engineer and take over an existing API then that is what you must do and that is a very valid use case. Looking through the docs on (HttpRule)[https://github.com/googleapis/googleapis/blob/master/google/api/http.proto], you almost want a "reply body" parameter attached. I haven't seen anything like that, but it might be a thing upstream is receptive to. If you could get it in the HttpRule proto then I would be 1000% in favor of supporting it. How can I help with this? |
No objection here. In general, I like the API Design Guide myself. But yeah, it's about adopting an existing API.
I believe that an attempt to add I guess I can ask upstream internally. |
Personally this seems like breaking a bit too far away from the underlying protobuf definition. It throws backwards compatibility to the wind, and I can't even begin to think of all the corner cases this is going to trigger. Given that this is a field property, have we considered how it might interact with query strings, partial path fields etc? It seems like a can of worms to me. What if you specify it twice? I understand the problem you're trying to solve but I'd be more inclined for us to have a special message type for this kind of thing. Maybe |
How? This can be a 'considered harmful' compatibility hack to be able to implement certain pre-existing APIs.
How? If you don't specify the new
But, how do query strings and partial path fields apply if this only changes the response? No interaction with the request messages.
I considered it; I think it can depend on what's doable in implementation stage. I'm currently personally interested in behavior of a top-level field on a top-level response message getting promoted as a response itself. I guess if you specify it on a repeated message-type field which contains a repeated message-type field where the option is also specified, we could try moving that one step up, too, allowing for an array of arrays?
This at the very least invades the realm of the gRPC API design; you have to change how your gRPC API looks, when in fact you only want to change the REST API. It means constructing the response becomes very unwieldy. An |
Fair enough
I don't mean backwards compatibility with the current gateway, just that if you have a service as you suggest and add a new field to your response message, that's impossible? It's breaking backwards compatibility guarantees given by protobuf. Adding new fields should always be possible, and for that to be true, there has to be a top level messages.
How do we tell the user this only applies to responses? Adding a field option that only applies in a subset of circumstances will need to obviously not apply in cases where it's not supposed to be used, or we might mislead users.
This seems like a nightmare to implement, are we going to allow this nesting indefinitely? Are we going to just document the limits? What if you put it on a map? A oneof? An enum? We could document that it only works in very specific well-defined circumstances, but it feels a bit like a hack unless we can make it universally applicable.
This is a fair point - but constructing messages for specific JSON structures is already what the official well known types encourage us to do. The whole point of the
I just want to make sure we consider all potential use cases of such an option and document the limitations. |
Thanks for the lively discussion -- but maybe we're straying into the "you are wrong for needing this feature" territory? :)
But this is not protobuf, this is JSON and it is a risk the developer can take. Again, this is a hack for implementing other, preexisting APIs -- something I would strongly discourage for new APIs.
In documentation and docstring for the option, I'd assume? How do we describe to the user how our view of the RFC6570
Recursion is a thing, no? But how deeply this is doable depends on the implementation -- which I didn't look at for now, as I wanted to gauge useful feedback on whether to spend time implementing this.
Yes. I foresee either limit of 1 or no limit.
I'm not sure what you're asking here. If you put it on a map (I don't know, does grpc-gateway support maps?) you clearly promote the map to a top level object. If you put it on a oneof -- well, how do we encode it in JSON now? Same for enum. Same for string. Same for int. JSON doesn't ban you from any of those being top level values, to my knowledge?
Yes, if that ends up being the implementation.
It is a hack that I would prefer not doing, but existing APIs are returning this today. I am interested in implementing a portion of Mastodon APIs in gRPC + gRPC-Gateway, but I am today blocked on one of the core APIs -- the one that lists statuses posted by a user account -- returning a list. Any workaround I would do would be a way, way worse hack and much less useful than just upstreaming support relevant annotations for this into gRPC-Gateway.
What gRPC-Gateway does is supposed to be a "predictable rendering" of JSON, already, no? Using
Sure. What other use cases do you propose users might have? My purpose for this bug was to hear about what other elegant ways there might be to expose this functionality to developers. Is Any of these helps me annotate the strange behavior once (in the proto), while keeping generated implementation and swagger definition in sync. I am not sure "developer won't know how to hold the phone right" is useful to decide on the implementation (they can design a broken API in a hundred different ways already) nor is "we must prevent the user from shooting themselves in foot in every way" or "we cannot possibly implement this without covering every single edge case". Can you provide specific use cases, and specific confusing .proto files that you believe need clarification before implementing and merging something that would support this? |
I apologize if my replies come off as anything other than constructive criticism. Isn't this actually possible to solve with .ListValue? Yes the method will be stupid to use for gRPC clients but you could expose two different methods, one wrapping the gRPC method and performing the conversion to and from the .ListValue and .Struct types. Is that too bad? There's always a cost to adding new features so it's not like it's a no brainer just because it can be done. |
One of the goals is to generate OpenAPI representation from the same source. Unless I am missing something, a |
This is true, so is that the real question then? This feature would allow the generated OpenAPI type signature to show repeated types returned, as opposed to having a generic list type. In the light of that, should we not consider adding a swagger specific annotation instead? It would mean the types in the protobuf definition wouldn't match the signature of the function in the swagger API, but at the same time it would avoid us having to create an official message descriptor extension. What do you think? |
If we introduce a new gRPC-Gateway-specific option on a
It would, unfortunately, also mean having to update Why not have a short-and-simple, nicely-named annotation that says "we need to strip the outer dictionary", and which is correctly understood by both |
To paste a shorter variant of what I put into #712: I suggest a careful examination of what's currently present in google.api.HttpRule documentation. |
@ivucica I can see you're being intentionally ambiguous, which is fine, but I'm interpreting this as you implying that this kind of thing exists-but-doesnt-exist-publically. That suggestion makes me a bit happier to accept this as it gives me some confidence it has been considered by a larger audience and corner cases dealt with, if any. So we're back to a gateway specified field option, allowed only for one field per message, repeated or not, and only at the top level of any response hierarchy? On a side note, that documentation is hilariously obviously not supposed to be public 😂.
Oh boy. |
If we want to do as upstream did, then it's attached to an I don't mind attaching to
Psst! Relevant organs have been notified |
Attaching to an |
Interesting design requirement was noted in #712 and a good reason for the new field in @doroginin n.b. I expect I will file the request for the |
@doroginin I have filed a request for a new option ID. |
No need for the option ID or for further hackiness -- #712 will be able to go through once upstream changes land and then we can look at closing this FR. ...this is less work for me than I expected ;) |
This sounds like the best outcome for everyone, nice! |
@ivucica and @johanbrandhorst, you two are the absolute best! Thanks for caring so much about this project 😃 |
I believe this is fixed in #712 |
Often pre-existing APIs return a list as a top-level JSON entity. Sometimes, the returned value may be a string or a number.
Example of an API that returns an array is Mastodon's Getting who an account is following call. I've run into other APIs with the same issue.
While I believe the default behavior should be as-is, It would be useful to allow promoting a field of a message to be the actual value of a message.
There are multiple ways to do it; we could add an option to a particular RPC, and we could add a grpc-gateway-specific option to a message. I'm leaning towards attaching it to a message as that allows doing this multiple times.
Proposing the following:
This should replace the following default behavior:
with the following:
Implementing the filter in the gRPC-Gateway serving binary is reasonably trivial. I've taken
net/http/httptest
, recorded the output, unmarshalled the JSON into amap[string]interface{}
, taken theaccount
key, encoded it into JSON and then output it as the response. It's tolerable.But it is a hack, and it doesn't affect the
protoc-gen-swagger
-generated definition.Since APIs that return arrays as a top level entity, it would be excellent if we could agree on an
option
-based design that would be respected by bothprotoc-gen-grpc-gateway
and byprotoc-gen-swagger
.Is this already possible? Did I miss something? Did I miss an open issue tracking this feature request? Thoughts on where the
option
would belong -- on therpc
or on themessage
?The text was updated successfully, but these errors were encountered: