Repository: goadesign/goa
Pull request #3167 (2022-10-12 19:53:46)

Make Goa proto wrapper field optional for arrays
Since proto will serialize empty arrays as nil.


Patch:
diff --git a/grpc/codegen/protobuf.go b/grpc/codegen/protobuf.go
index e5eedede..417b5e67 100644
--- a/grpc/codegen/protobuf.go
+++ b/grpc/codegen/protobuf.go
@@ -66,14 +66,14 @@ func makeProtoBufMessage(att *expr.AttributeExpr, tname string, sd *ServiceData)
 		}
 		return att
 	case expr.IsPrimitive(att.Type):
-		wrapAttr(att, tname, sd)
+		wrapAttr(att, tname, true, sd)
 		return att
 	case isut:
 		if expr.IsArray(ut) {
-			wrapAttr(att, tname, sd)
+			wrapAttr(att, tname, false, sd)
 		}
 	case expr.IsArray(att.Type) || expr.IsMap(att.Type):
-		wrapAttr(att, tname, sd)
+		wrapAttr(att, tname, false, sd)
 	case expr.IsObject(att.Type) || expr.IsUnion(att.Type):
 		att.Type = &expr.UserTypeExpr{
 			TypeName:      tname,
@@ -102,12 +102,12 @@ func makeProtoBufMessageR(att *expr.AttributeExpr, tname *string, sd *ServiceDat
 		switch {
 		case expr.IsArray(att.Type):
 			wrapAttr(att, "ArrayOf"+tname+
-				protoBufify(protoBufMessageDef(expr.AsArray(att.Type).ElemType, sd), true, true), sd)
+				protoBufify(protoBufMessageDef(expr.AsArray(att.Type).ElemType, sd), true, true), true, sd)
 		case expr.IsMap(att.Type):
 			m := expr.AsMap(att.Type)
 			wrapAttr(att, tname+"MapOf"+
 				protoBufify(protoBufMessageDef(m.KeyType, sd), true, true)+
-				protoBufify(protoBufMessageDef(m.ElemType, sd), true, true), sd)
+				protoBufify(protoBufMessageDef(m.ElemType, sd), true, true), true, sd)
 		}
 	}
 
@@ -116,7 +116,7 @@ func makeProtoBufMessageR(att *expr.AttributeExpr, tname *string, sd *ServiceDat
 		return
 	case isut:
 		if expr.IsArray(ut) {
-			wrapAttr(ut.Attribute(), ut.Name(), sd)
+			wrapAttr(ut.Attribute(), ut.Name(), false, sd)
 		}
 		makeProtoBufMessageR(ut.Attribute(), tname, sd, seen)
 	case expr.IsArray(att.Type):
@@ -140,9 +140,9 @@ func makeProtoBufMessageR(att *expr.AttributeExpr, tname *string, sd *ServiceDat
 
 // wrapAttr makes the attribute type a user type by wrapping the given
 // attribute into an attribute named "field".
-func wrapAttr(att *expr.AttributeExpr, tname string, sd *ServiceData) {
+func wrapAttr(att *expr.AttributeExpr, tname string, req bool, sd *ServiceData) {
 	wrap := func(attr *expr.AttributeExpr) *expr.AttributeExpr {
-		return &expr.AttributeExpr{
+		res := &expr.AttributeExpr{
 			Type: &expr.Object{
 				&expr.NamedAttributeExpr{
 					Name: "field",
@@ -153,10 +153,13 @@ func wrapAttr(att *expr.AttributeExpr, tname string, sd *ServiceData) {
 					},
 				},
 			},
-			Validation: &expr.ValidationExpr{
+		}
+		if req {
+			res.Validation = &expr.ValidationExpr{
 				Required: []string{"field"},
-			},
+			}
 		}
+		return res
 	}
 	switch dt := att.Type.(type) {
 	case expr.UserType:


Test patch:
diff --git a/grpc/codegen/testdata/client_type_code.go b/grpc/codegen/testdata/client_type_code.go
index 82a2a108..b8e414ce 100644
--- a/grpc/codegen/testdata/client_type_code.go
+++ b/grpc/codegen/testdata/client_type_code.go
@@ -183,35 +183,6 @@ func NewMethodResultWithCollectionResult(message *service_result_with_collection
 	return result
 }
 
-// ValidateMethodResultWithCollectionResponse runs the validations defined on
-// MethodResultWithCollectionResponse.
-func ValidateMethodResultWithCollectionResponse(message *service_result_with_collectionpb.MethodResultWithCollectionResponse) (err error) {
-	if message.Result != nil {
-		if err2 := ValidateResultT(message.Result); err2 != nil {
-			err = goa.MergeErrors(err, err2)
-		}
-	}
-	return
-}
-
-// ValidateResultT runs the validations defined on ResultT.
-func ValidateResultT(result *service_result_with_collectionpb.ResultT) (err error) {
-	if result.CollectionField != nil {
-		if err2 := ValidateRTCollection(result.CollectionField); err2 != nil {
-			err = goa.MergeErrors(err, err2)
-		}
-	}
-	return
-}
-
-// ValidateRTCollection runs the validations defined on RTCollection.
-func ValidateRTCollection(collectionField *service_result_with_collectionpb.RTCollection) (err error) {
-	if collectionField.Field == nil {
-		err = goa.MergeErrors(err, goa.MissingFieldError("field", "collectionField"))
-	}
-	return
-}
-
 // svcServiceresultwithcollectionResultTToServiceResultWithCollectionpbResultT
 // builds a value of type *service_result_with_collectionpb.ResultT from a
 // value of type *serviceresultwithcollection.ResultT.
diff --git a/grpc/codegen/testdata/request_decoder_code.go b/grpc/codegen/testdata/request_decoder_code.go
index 7b179789..bfa3d323 100644
--- a/grpc/codegen/testdata/request_decoder_code.go
+++ b/grpc/codegen/testdata/request_decoder_code.go
@@ -32,9 +32,6 @@ func DecodeMethodUnaryRPCNoResultRequest(ctx context.Context, v interface{}, md
 		if message, ok = v.(*service_unary_rpc_no_resultpb.MethodUnaryRPCNoResultRequest); !ok {
 			return nil, goagrpc.ErrInvalidType("ServiceUnaryRPCNoResult", "MethodUnaryRPCNoResult", "*service_unary_rpc_no_resultpb.MethodUnaryRPCNoResultRequest", v)
 		}
-		if err := ValidateMethodUnaryRPCNoResultRequest(message); err != nil {
-			return nil, err
-		}
 	}
 	var payload []string
 	{
diff --git a/grpc/codegen/testdata/streaming_code.go b/grpc/codegen/testdata/streaming_code.go
index f9606ca5..7383e3a4 100644
--- a/grpc/codegen/testdata/streaming_code.go
+++ b/grpc/codegen/testdata/streaming_code.go
@@ -172,9 +172,6 @@ func (s *MethodServerStreamingArrayClientStream) Recv() ([]int, error) {
 	if err != nil {
 		return res, err
 	}
-	if err = ValidateMethodServerStreamingArrayResponse(v); err != nil {
-		return res, err
-	}
 	return NewMethodServerStreamingArrayResponse(v), nil
 }
 `
@@ -197,9 +194,6 @@ func (s *MethodServerStreamingMapClientStream) Recv() (map[string]*serviceserver
 	if err != nil {
 		return res, err
 	}
-	if err = ValidateMethodServerStreamingMapResponse(v); err != nil {
-		return res, err
-	}
 	return NewMethodServerStreamingMapResponse(v), nil
 }
 `