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

how to provide interface type inside proto for grpc-gateway #723

Closed
vtolstov opened this issue Aug 13, 2018 · 15 comments
Closed

how to provide interface type inside proto for grpc-gateway #723

vtolstov opened this issue Aug 13, 2018 · 15 comments

Comments

@vtolstov
Copy link

I have json message with some field image that can holds uint32 or string.
How can i specify such thing inside proto file? Or how to not decode this filed and get raw data for it?

@achew22
Copy link
Collaborator

achew22 commented Aug 13, 2018

Unfortunately, proto doesn't really support union types. The only way I can think of doing this is to always take a string. I believe the json parser is nice enough to cast numbers into strings but I'm not 100% sure on that. You might also consider looking into one_of types, but I don't think they exactly provide these semantics.

@vtolstov
Copy link
Author

oneof not helps:

  oneof image {
    string slug = 4;
    uint64 id = 5;
  };
{"error":"json: cannot unmarshal string into Go value of type pb.XXX","code":3}

@vtolstov
Copy link
Author

if i'm provide string, i have error that can't unmarshal to my type.

{"error":"json: cannot unmarshal number into Go value of type string","code":3}

@achew22
Copy link
Collaborator

achew22 commented Aug 16, 2018

Huh, I guess it really doesn't support them. The only other option I can think of is writing your own marshaller for jspb or writing an interceptor in http land that rewrites the request. Sorry!

@vtolstov
Copy link
Author

@achew22 nice, can you provide link to doc how to write such interceptor?

@achew22
Copy link
Collaborator

achew22 commented Aug 16, 2018

Unfortunately I don't think we have docs that demonstrate that. If you decide to go that route, it would be great if you could add some documentation into the /docs directory

@johanbrandhorst
Copy link
Collaborator

@vtolstov Just to add to what @achew22 already wrote, I think what this means is just implementing a http.Handler that looks at incoming requests and rewrites them such that they are in a format which is compatible with the normal gRPC-Gateway handlers, then wrap the gateway runtime mux with that handler before attaching it to a http.Server.

@johanbrandhorst
Copy link
Collaborator

And the same for any replies, of course. It's called http Middleware: https://hackernoon.com/simple-http-middleware-with-go-79a4ad62889b.

@ivucica
Copy link
Collaborator

ivucica commented Aug 17, 2018

Random thought, partially based on @achew22's comment (I almost suggested implementing UnmarshalJSON until I realized that's stupid):

  • given something.proto containing message IntAndString, generate your something.pb.go in a package, e.g. github.com/vtolstov/something/proto.

  • create a new something.custom.go in the same package e.g. github.com/vtolstov/something/proto.

  • in something.custom.go, implement new method UnmarshalJSONPB() from the github.com/golang/protobuf/jsonpb#JSONPBUnmarshaler interface on the proto type IntAndString:

      func (a *IntAndString) UnmarshalJSONPB(*Unmarshaler, []byte) error {
        if b[0] == '"' {
          // handle as string
          a.StringValue = ...
        } else {
          // handle as number, e.g. maybe something like strconv.Atoi(string(b[1:len(b)-2]))
          a.NumberValue = ....
        }
        return nil
      }
    
  • when you need such a union, IntAndString some_value = 19;

I have, obviously, not tried this, and I have definitely not run this.

@vtolstov
Copy link
Author

@ivucica thanks! i'm try this

@achew22
Copy link
Collaborator

achew22 commented Aug 17, 2018

That's a very clever idea! Good thinking @ivucica

@vtolstov
Copy link
Author

vtolstov commented Aug 17, 2018

@ivucica not worked =(
May be i'm wrong...
dir pb contains common.go

package pb

import (
        "fmt"
        "strconv"

        "github.com/gogo/protobuf/jsonpb"
)

func (v *IntAndString) UnmarshalJSONPB(c *jsonpb.Unmarshaler, b []byte) error {
        fmt.Printf("AAAAA\n")
        if i, err := strconv.Atoi(string(b)); err != nil {
                v.Str = string(b)
        } else {
                v.Num = uint32(i)
        }
        return nil
}

func (v *IntAndString) MarshalJSONPB(c *jsonpb.Marshaler) ([]byte, error) {
        return nil, nil
}

define IntAndString in proto file like:

message IntAndString {
  string str = 1;
  uint32 num = 2;
};

in other proto file use IntAndString image = 5;

when i'm send int i have :

{"code":3,"message":"json: cannot unmarshal number into Go value of type map[string]json.RawMessage","details":[]}

if i don't define in proto type IntAndString i fails to generate:

"IntAndString" is not defined

@vtolstov
Copy link
Author

ping...

@ivucica
Copy link
Collaborator

ivucica commented Aug 27, 2018

ping...

I won't get around to playing with this out any time soon.

What have you tried to triage this? Does IntAndString's UnmarshalJSONPB get called? Trivially, you can just add a fmt.Println() to it and see what happens.

I am also confused at which map[string]json.RawMessage is this being unmarshalled into. It would be interesting to find where this error comes from. Have you tried finding out why map[string]json.RawMessage] is involved?

For instance, you can use gdb for debugging, or even better use Delve. I have done neither, but if I needed to triage what happens, I would find the source code line that prints cannot unmarshal number into Go value of type, add a breakpoint to it, then get the backtrace (in GDB, that's bt).


Once again, I have not used a debugger with Go code, and I have not tried to do what you want to do here. I think UnmarshalJSONPB should work, but if you can't make it work and if you're on a deadline of any sort, intercept the API endpoint manually and construct the gRPC call manually.


if i don't define in proto type IntAndString i fails to generate:

"IntAndString" is not defined

That makes sense, right? type instruction is in the generated code, in .pb.go. If you delete it, the type IntAndString goes away with it, right? So if there's no IntAndString type anymore, it makes no sense to attach UnmarshalJSONPB to it, right?

@johanbrandhorst
Copy link
Collaborator

This is not an issue with the library itself, so I will close this, though it contains some interesting information. Please feel free to reply in-line again, or make a case for keeping this open :).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants