Skip to content

Commit

Permalink
fix: regapic support for proto wkt in query params (#1124)
Browse files Browse the repository at this point in the history
* fix: regapic support for proto wkt in qp

* include other WKT

* remove extraneous import

* add comment for test client init

* paramAdd in if-else

* fix go.mod & go.sum

* fix go.sum
  • Loading branch information
noahdietz authored Sep 6, 2022
1 parent e50fcd3 commit f000c98
Show file tree
Hide file tree
Showing 10 changed files with 340 additions and 140 deletions.
46 changes: 42 additions & 4 deletions internal/gengapic/genrest.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ var gRPCToHTTP map[code.Code]string = map[code.Code]string{
code.Code_DATA_LOSS: "http.StatusInternalServerError",
}

var wellKnownTypes = []string{
".google.protobuf.FieldMask",
".google.protobuf.Timestamp",
".google.protobuf.Duration",
".google.protobuf.DoubleValue",
".google.protobuf.FloatValue",
".google.protobuf.Int64Value",
".google.protobuf.UInt64Value",
".google.protobuf.Int32Value",
".google.protobuf.UInt32Value",
".google.protobuf.BoolValue",
".google.protobuf.StringValue",
".google.protobuf.BytesValue",
".google.protobuf.Value",
".google.protobuf.ListValue",
}

func lowcaseRestClientName(servName string) string {
if servName == "" {
return "restClient"
Expand Down Expand Up @@ -365,7 +382,7 @@ func (g *generator) getLeafs(msg *descriptor.DescriptorProto, excludedFields ...
m *descriptor.DescriptorProto,
) {
for _, field := range m.GetField() {
if field.GetType() == descriptor.FieldDescriptorProto_TYPE_MESSAGE {
if field.GetType() == fieldTypeMessage && !strContains(wellKnownTypes, field.GetTypeName()) {
handleMsg(field, stack)
} else {
handleLeaf(field, stack)
Expand Down Expand Up @@ -403,8 +420,25 @@ func (g *generator) generateQueryString(m *descriptor.MethodDescriptorProto) {
singularPrimitive := field.GetType() != fieldTypeMessage &&
field.GetType() != fieldTypeBytes &&
field.GetLabel() != fieldLabelRepeated
g.imports[pbinfo.ImportSpec{Path: "fmt"}] = true
paramAdd := fmt.Sprintf("params.Add(%q, fmt.Sprintf(%q, req%s))", lowerFirst(snakeToCamel(path)), "%v", accessor)

var paramAdd string
// Handle well known protobuf types with special JSON encodings.
if strContains(wellKnownTypes, field.GetTypeName()) {
b := strings.Builder{}
b.WriteString(fmt.Sprintf("%s, err := protojson.Marshal(req%s)\n", field.GetJsonName(), accessor))
b.WriteString("if err != nil {\n")
if m.GetOutputType() == emptyType {
b.WriteString(fmt.Sprintf(" return err\n"))
} else {
b.WriteString(fmt.Sprintf(" return nil, err\n"))
}
b.WriteString("}\n")
b.WriteString(fmt.Sprintf("params.Add(%q, string(%s))", lowerFirst(snakeToCamel(path)), field.GetJsonName()))
paramAdd = b.String()
} else {
paramAdd = fmt.Sprintf("params.Add(%q, fmt.Sprintf(%q, req%s))", lowerFirst(snakeToCamel(path)), "%v", accessor)
g.imports[pbinfo.ImportSpec{Path: "fmt"}] = true
}

// Only required, singular, primitive field types should be added regardless.
if required && singularPrimitive {
Expand Down Expand Up @@ -437,7 +471,11 @@ func (g *generator) generateQueryString(m *descriptor.MethodDescriptorProto) {
p(`if req%s != 0 {`, accessor)
}
}
p(" %s", paramAdd)

// Split on newline so that multi-line param adders will be formatted properly.
for _, s := range strings.Split(paramAdd, "\n") {
p(" %s", s)
}
p("}")
}

Expand Down
73 changes: 73 additions & 0 deletions internal/gengapic/genrest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,18 @@ func TestLeafFields(t *testing.T) {
},
}

wellKnownMsg := &descriptor.DescriptorProto{
Name: proto.String("Update"),
Field: []*descriptor.FieldDescriptorProto{
{
Name: proto.String("update_mask"),
Number: proto.Int32(int32(0)),
Type: typep(descriptor.FieldDescriptorProto_TYPE_MESSAGE),
TypeName: proto.String(".google.protobuf.FieldMask"),
},
},
}

file := &descriptor.FileDescriptorProto{
Package: proto.String("animalia.mollusca"),
Options: &descriptor.FileOptions{
Expand All @@ -349,6 +361,7 @@ func TestLeafFields(t *testing.T) {
complexMsg,
recursiveMsg,
overarchingMsg,
wellKnownMsg,
},
}
req := plugin.CodeGeneratorRequest{
Expand Down Expand Up @@ -406,6 +419,13 @@ func TestLeafFields(t *testing.T) {
"mass_kg": overarchingMsg.GetField()[1],
},
},
{
name: "well_known_message_test",
msg: wellKnownMsg,
expected: map[string]*descriptor.FieldDescriptorProto{
"update_mask": wellKnownMsg.GetField()[0],
},
},
} {
actual := g.getLeafs(tst.msg, tst.excludedFields...)
if diff := cmp.Diff(actual, tst.expected, cmp.Comparer(proto.Equal)); diff != "" {
Expand Down Expand Up @@ -466,6 +486,25 @@ func TestGenRestMethod(t *testing.T) {
}
pagedFooResFQN := fmt.Sprintf(".%s.PagedFooResponse", pkg)

fooField := &descriptor.FieldDescriptorProto{
Name: proto.String("foo"),
Type: descriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: proto.String(foofqn),
}

maskField := &descriptor.FieldDescriptorProto{
Name: proto.String("update_mask"),
JsonName: proto.String("updateMask"),
Type: descriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
TypeName: proto.String(".google.protobuf.FieldMask"),
}

updateReq := &descriptor.DescriptorProto{
Name: proto.String("UpdateRequest"),
Field: []*descriptor.FieldDescriptorProto{fooField, maskField},
}
updateReqFqn := fmt.Sprintf(".%s.UpdateRequest", pkg)

nameOpts := &descriptor.FieldOptions{}
proto.SetExtension(nameOpts, extendedops.E_OperationField, extendedops.OperationResponseMapping_NAME)
nameField := &descriptor.FieldDescriptorProto{
Expand Down Expand Up @@ -600,6 +639,21 @@ func TestGenRestMethod(t *testing.T) {
Options: httpBodyRPCOpt,
}

updateRPCOpt := &descriptor.MethodOptions{}
proto.SetExtension(updateRPCOpt, annotations.E_Http, &annotations.HttpRule{
Pattern: &annotations.HttpRule_Post{
Post: "/v1/foo",
},
Body: "foo",
})

updateRPC := &descriptor.MethodDescriptorProto{
Name: proto.String("UpdateRPC"),
InputType: proto.String(updateReqFqn),
OutputType: proto.String(foofqn),
Options: updateRPCOpt,
}

s := &descriptor.ServiceDescriptorProto{
Name: proto.String("FooService"),
}
Expand Down Expand Up @@ -633,12 +687,14 @@ func TestGenRestMethod(t *testing.T) {
opS: f,
opRPC: f,
lroRPC: f,
updateRPC: f,
foo: f,
s: f,
pagedFooReq: f,
pagedFooRes: f,
lroDesc: protodesc.ToFileDescriptorProto(longrunning.File_google_longrunning_operations_proto),
httpBodyDesc: protodesc.ToFileDescriptorProto(httpbody.File_google_api_httpbody_proto),
updateReq: f,
},
ParentElement: map[pbinfo.ProtoType]pbinfo.ProtoType{
opRPC: s,
Expand All @@ -649,9 +705,11 @@ func TestGenRestMethod(t *testing.T) {
clientStreamRPC: s,
lroRPC: s,
httpBodyRPC: s,
updateRPC: s,
nameField: op,
sizeField: foo,
otherField: foo,
maskField: updateReq,
},
Type: map[string]pbinfo.ProtoType{
opfqn: op,
Expand All @@ -661,6 +719,7 @@ func TestGenRestMethod(t *testing.T) {
pagedFooResFQN: pagedFooRes,
lroType: lroDesc,
httpBodyType: httpBodyDesc,
updateReqFqn: updateReq,
},
},
}
Expand Down Expand Up @@ -789,6 +848,20 @@ func TestGenRestMethod(t *testing.T) {
{Name: "httpbodypb", Path: "google.golang.org/genproto/googleapis/api/httpbody"}: true,
},
},
{
name: "update_rpc",
method: updateRPC,
options: &options{restNumericEnum: true},
imports: map[pbinfo.ImportSpec]bool{
{Path: "bytes"}: true,
{Path: "fmt"}: true,
{Path: "google.golang.org/protobuf/encoding/protojson"}: true,
{Path: "io/ioutil"}: true,
{Path: "google.golang.org/api/googleapi"}: true,
{Path: "net/url"}: true,
{Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true,
},
},
} {
s.Method = []*descriptor.MethodDescriptorProto{tst.method}
g.opts = tst.options
Expand Down
68 changes: 68 additions & 0 deletions internal/gengapic/testdata/rest_UpdateRPC.want
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
func (c *fooRESTClient) UpdateRPC(ctx context.Context, req *foopb.UpdateRequest, opts ...gax.CallOption) (*foopb.Foo, error) {
m := protojson.MarshalOptions{AllowPartial: true, UseEnumNumbers: true}
body := req.GetFoo()
jsonReq, err := m.Marshal(body)
if err != nil {
return nil, err
}

baseUrl, err := url.Parse(c.endpoint)
if err != nil {
return nil, err
}
baseUrl.Path += fmt.Sprintf("/v1/foo")

params := url.Values{}
params.Add("$alt", "json;enum-encoding=int")
if req.GetUpdateMask() != nil {
updateMask, err := protojson.Marshal(req.GetUpdateMask())
if err != nil {
return nil, err
}
params.Add("updateMask", string(updateMask))
}

baseUrl.RawQuery = params.Encode()

// Build HTTP headers from client and context metadata.
headers := buildHeaders(ctx, c.xGoogMetadata, metadata.Pairs("Content-Type", "application/json"))
opts = append((*c.CallOptions).UpdateRPC[0:len((*c.CallOptions).UpdateRPC):len((*c.CallOptions).UpdateRPC)], opts...)
unm := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
resp := &foopb.Foo{}
e := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
if settings.Path != "" {
baseUrl.Path = settings.Path
}
httpReq, err := http.NewRequest("POST", baseUrl.String(), bytes.NewReader(jsonReq))
if err != nil {
return err
}
httpReq = httpReq.WithContext(ctx)
httpReq.Header = headers

httpRsp, err := c.httpClient.Do(httpReq)
if err != nil{
return err
}
defer httpRsp.Body.Close()

if err = googleapi.CheckResponse(httpRsp); err != nil {
return err
}

buf, err := ioutil.ReadAll(httpRsp.Body)
if err != nil {
return err
}

if err := unm.Unmarshal(buf, resp); err != nil {
return maybeUnknownEnum(err)
}

return nil
}, opts...)
if e != nil {
return nil, e
}
return resp, nil
}
1 change: 1 addition & 0 deletions showcase/compliance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"google.golang.org/protobuf/proto"
)

// Client is initialized in main_test.go.
var complianceClient *showcase.ComplianceClient

// method is the function type that implements any of the Compliance.RepeatData* RPCs, which all have the same signature.
Expand Down
7 changes: 5 additions & 2 deletions showcase/echo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ import (
"google.golang.org/protobuf/types/known/durationpb"
)

var echo *showcase.EchoClient
var echoREST *showcase.EchoClient
// Clients are initialized in main_test.go.
var (
echo *showcase.EchoClient
echoREST *showcase.EchoClient
)

func TestEcho(t *testing.T) {
defer check(t)
Expand Down
2 changes: 1 addition & 1 deletion showcase/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.19
require (
cloud.google.com/go v0.104.0
github.com/google/go-cmp v0.5.8
github.com/googleapis/gapic-showcase v0.24.0
github.com/googleapis/gapic-showcase v0.25.0
github.com/googleapis/gax-go/v2 v2.5.1
google.golang.org/api v0.95.0
google.golang.org/genproto v0.0.0-20220902135211-223410557253
Expand Down
18 changes: 9 additions & 9 deletions showcase/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2Z
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
cloud.google.com/go v0.103.0/go.mod h1:vwLx1nqLrzLX/fpwSMOXmFIqBOyHsvHbnAdbGSJ+mKk=
cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8=
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
Expand Down Expand Up @@ -214,8 +213,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/gapic-showcase v0.24.0 h1:YDNng6apgYYbw5AKTUdBLQ8pk5QjNAdHRSvd5sdRZSs=
github.com/googleapis/gapic-showcase v0.24.0/go.mod h1:pBv821ueIBdk6qC9nrBZePZYBG9VdutF2omq3dlp5mU=
github.com/googleapis/gapic-showcase v0.25.0 h1:Ui32sbsvvmvpQok07xMwrAI0yDBlruLwcJ10Mn9CyNs=
github.com/googleapis/gapic-showcase v0.25.0/go.mod h1:lkwLil+VrQ5vrOL8+rexI+VZSG2QmzS3W2jaygrGu58=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
Expand Down Expand Up @@ -512,7 +511,6 @@ golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -528,7 +526,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -731,8 +729,9 @@ google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3
google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko=
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.89.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.94.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
google.golang.org/api v0.95.0 h1:d1c24AAS01DYqXreBeuVV7ewY/U8Mnhh47pwtsgVtYg=
google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
Expand Down Expand Up @@ -829,8 +828,9 @@ google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljW
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220902135211-223410557253 h1:vXJMM8Shg7TGaYxZsQ++A/FOSlbDmDtWhS/o+3w/hj4=
google.golang.org/genproto v0.0.0-20220902135211-223410557253/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
Expand Down
Loading

0 comments on commit f000c98

Please sign in to comment.