Repository: googleapis/gapic-generator-go
Pull request #994 (2022-05-12 23:31:46)

fix(gengapic): REGAPIC encode enums as numbers
Part of our REGAPIC effort to standardize JSON (de)serialization of enums as numbers to avoid the "unknown enum" error that comes with using the string identifiers instead.

Note: I've removed the use of `UseProtoNames: false` option because it is `false` by default so stating that explicitly is unnecessary and it was done inconsistently.


Patch:
diff --git a/internal/gengapic/genrest.go b/internal/gengapic/genrest.go
index f69e7c9..d98526c 100644
--- a/internal/gengapic/genrest.go
+++ b/internal/gengapic/genrest.go
@@ -548,7 +548,7 @@ func (g *generator) serverStreamRESTCall(servName string, s *descriptor.ServiceD
 		if verb == http.MethodGet || verb == http.MethodDelete {
 			return fmt.Errorf("invalid use of body parameter for a get/delete method %q", m.GetName())
 		}
-		p("m := protojson.MarshalOptions{AllowPartial: true}")
+		g.protoJSONMarshaler()
 		requestObject := "req"
 		if info.body != "*" {
 			requestObject = "body"
@@ -733,7 +733,7 @@ func (g *generator) pagingRESTCall(servName string, m *descriptor.MethodDescript
 
 	maybeReqBytes := "nil"
 	if info.body != "" {
-		p("m := protojson.MarshalOptions{AllowPartial: true, UseProtoNames: false}")
+		g.protoJSONMarshaler()
 		maybeReqBytes = "bytes.NewReader(jsonReq)"
 		g.imports[pbinfo.ImportSpec{Path: "bytes"}] = true
 	}
@@ -846,7 +846,7 @@ func (g *generator) lroRESTCall(servName string, m *descriptor.MethodDescriptorP
 		if verb == http.MethodGet || verb == http.MethodDelete {
 			return fmt.Errorf("invalid use of body parameter for a get/delete method %q", m.GetName())
 		}
-		p("m := protojson.MarshalOptions{AllowPartial: true}")
+		g.protoJSONMarshaler()
 		requestObject := "req"
 		if info.body != "*" {
 			requestObject = "body"
@@ -952,7 +952,7 @@ func (g *generator) emptyUnaryRESTCall(servName string, m *descriptor.MethodDesc
 		if verb == http.MethodGet || verb == http.MethodDelete {
 			return fmt.Errorf("invalid use of body parameter for a get/delete method %q", m.GetName())
 		}
-		p("m := protojson.MarshalOptions{AllowPartial: true, UseProtoNames: false}")
+		g.protoJSONMarshaler()
 		requestObject := "req"
 		if info.body != "*" {
 			requestObject = "body"
@@ -1045,7 +1045,7 @@ func (g *generator) unaryRESTCall(servName string, m *descriptor.MethodDescripto
 		if verb == http.MethodGet || verb == http.MethodDelete {
 			return fmt.Errorf("invalid use of body parameter for a get/delete method %q", m.GetName())
 		}
-		p("m := protojson.MarshalOptions{AllowPartial: true}")
+		g.protoJSONMarshaler()
 		requestObject := "req"
 		if info.body != "*" {
 			requestObject = "body"
@@ -1126,3 +1126,16 @@ func (g *generator) unaryRESTCall(servName string, m *descriptor.MethodDescripto
 	g.imports[outSpec] = true
 	return nil
 }
+
+func (g *generator) protoJSONMarshaler() {
+	marshalOpts := "AllowPartial: true, UseEnumNumbers: true"
+	// Today, DIREGAPIC protos are generated to represent enum fields as strings
+	// except for the (very static) Operation.state field, which is kept as an
+	// enum. However, since they are synthetic protos, the enum numbers likely
+	// do not match the internal proto enum value, so we do not want to send
+	// any enums as numbers with DIREGAPIC clients.
+	if g.opts.diregapic {
+		marshalOpts = "AllowPartial: true"
+	}
+	g.pt.Printf("m := protojson.MarshalOptions{%s}", marshalOpts)
+}


Test patch:
diff --git a/internal/gengapic/testdata/rest_LongrunningRPC.want b/internal/gengapic/testdata/rest_LongrunningRPC.want
index d13f041..871c66d 100644
--- a/internal/gengapic/testdata/rest_LongrunningRPC.want
+++ b/internal/gengapic/testdata/rest_LongrunningRPC.want
@@ -1,5 +1,5 @@
 func (c *fooRESTClient) LongrunningRPC(ctx context.Context, req *foopb.Foo, opts ...gax.CallOption) (*LongrunningRPCOperation, error) {
-	m := protojson.MarshalOptions{AllowPartial: true}
+	m := protojson.MarshalOptions{AllowPartial: true, UseEnumNumbers: true}
 	jsonReq, err := m.Marshal(req)
 	if err != nil {
 		return nil, err
diff --git a/internal/gengapic/testdata/rest_ServerStreamRPC.want b/internal/gengapic/testdata/rest_ServerStreamRPC.want
index 278ecb2..5ea373f 100644
--- a/internal/gengapic/testdata/rest_ServerStreamRPC.want
+++ b/internal/gengapic/testdata/rest_ServerStreamRPC.want
@@ -1,5 +1,5 @@
 func (c *fooRESTClient) ServerStreamRPC(ctx context.Context, req *foopb.Foo, opts ...gax.CallOption) (foopb.FooService_ServerStreamRPCClient, error) {
-	m := protojson.MarshalOptions{AllowPartial: true}
+	m := protojson.MarshalOptions{AllowPartial: true, UseEnumNumbers: true}
 	jsonReq, err := m.Marshal(req)
 	if err != nil {
 		return nil, err
diff --git a/internal/gengapic/testdata/rest_UnaryRPC.want b/internal/gengapic/testdata/rest_UnaryRPC.want
index 118431c..2475495 100644
--- a/internal/gengapic/testdata/rest_UnaryRPC.want
+++ b/internal/gengapic/testdata/rest_UnaryRPC.want
@@ -1,5 +1,5 @@
 func (c *fooRESTClient) UnaryRPC(ctx context.Context, req *foopb.Foo, opts ...gax.CallOption) (*foopb.Foo, error) {
-	m := protojson.MarshalOptions{AllowPartial: true}
+	m := protojson.MarshalOptions{AllowPartial: true, UseEnumNumbers: true}
 	jsonReq, err := m.Marshal(req)
 	if err != nil {
 		return nil, err