Repository: goadesign/goa (go)

Issue:
Required Fields is not working on Payload
Hi,

I want to specify `Required` field inside the `Payload` as mentioned on docs:
```golang
//    Method("divide", func() {
//        // Specify additional required attributes on user type.
//        Payload(Operands, func() {
//            Required("left", "right")
//        })
//    })
//
func Payload(val interface{}, args ...interface{}) {
	if len(args) > 2 {
		eval.ReportError("too many arguments")
	}
	e, ok := eval.Current().(*expr.MethodExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	e.Payload = methodDSL("Payload", val, args...)
}
```

**goa version:** v3.5.4
**go version:** 1.14
here's my design:
```golang
var HelloType = Type("hello.type", func() {
	Token("token")
	Attribute("message", String, "message")
	Attribute("user", String, "the user")
})

...
	Method("post", func() {
		Payload(HelloType, func() {
			Required("user")
		})

		HTTP(func() {
			POST("/")
		})
	})
...
```

**Expected Result:** `user` field is required
**Actual Result:** `user` is not required, please check the following generated code of `HelloType`

```golang
// HelloType is the payload type of the hello service post method.
type HelloType struct {
	Token *string
	// message
	Message *string
	// the user
	User *string
}
```


Pull request:
Fix use of required on parent user type
For example:
```go
Payload(ParentType, func() { Required("attr") })
```
Make sure to copy required validation to user type's attribute as it is
used to generate the request payload type definition.

Fix #3026

Patch:
diff --git a/codegen/service/service_data.go b/codegen/service/service_data.go
index 904ec152..a697f893 100644
--- a/codegen/service/service_data.go
+++ b/codegen/service/service_data.go
@@ -609,21 +609,23 @@ func (d ServicesData) analyze(service *expr.ServiceExpr) *Data {
 	}
 
 	for _, t := range expr.Root.Types {
-		if svcs, ok := t.Attribute().Meta["type:generate:force"]; ok {
-			att := &expr.AttributeExpr{Type: t}
-			if len(svcs) > 0 {
-				// Force generate type only in the specified services
-				for _, svc := range svcs {
-					if svc == service.Name {
-						types = append(types, collectTypes(att, scope, seen)...)
-						break
-					}
+		svcs, ok := t.Attribute().Meta["type:generate:force"]
+		if !ok {
+			continue
+		}
+		att := &expr.AttributeExpr{Type: t}
+		if len(svcs) > 0 {
+			// Force generate type only in the specified services
+			for _, svc := range svcs {
+				if svc == service.Name {
+					types = append(types, collectTypes(att, scope, seen)...)
+					break
 				}
-			} else {
-				// Force generate type in all the services
-				types = append(types, collectTypes(att, scope, seen)...)
 			}
+			continue
 		}
+		// Force generate type in all the services
+		types = append(types, collectTypes(att, scope, seen)...)
 	}
 
 	var (
@@ -634,35 +636,37 @@ func (d ServicesData) analyze(service *expr.ServiceExpr) *Data {
 		methods = make([]*MethodData, len(service.Methods))
 		for i, e := range service.Methods {
 			m := buildMethodData(e, pkgName, service, scope)
-			if rt, ok := e.Result.Type.(*expr.ResultTypeExpr); ok {
-				var view string
-				if v, ok := e.Result.Meta["view"]; ok {
-					view = v[0]
-				}
-				if vrt, ok := seenViewed[m.Result+"::"+view]; ok {
-					m.ViewedResult = vrt
-				} else {
-					projected := seenProj[rt.ID()]
-					projAtt := &expr.AttributeExpr{Type: projected.Type}
-					vrt := buildViewedResultType(e.Result, projAtt, viewspkg, scope, viewScope)
-					found := false
-					for _, rt := range viewedRTs {
-						if rt.Type.ID() == vrt.Type.ID() {
-							found = true
-							break
-						}
-					}
-					if !found {
-						viewedRTs = append(viewedRTs, vrt)
-					}
-					m.ViewedResult = vrt
-					seenViewed[vrt.Name+"::"+view] = vrt
-				}
-			}
 			methods[i] = m
 			for _, s := range m.Schemes {
 				schemes = schemes.Append(s)
 			}
+			rt, ok := e.Result.Type.(*expr.ResultTypeExpr)
+			if !ok {
+				continue
+			}
+			var view string
+			if v, ok := e.Result.Meta["view"]; ok {
+				view = v[0]
+			}
+			if vrt, ok := seenViewed[m.Result+"::"+view]; ok {
+				m.ViewedResult = vrt
+				continue
+			}
+			projected := seenProj[rt.ID()]
+			projAtt := &expr.AttributeExpr{Type: projected.Type}
+			vrt := buildViewedResultType(e.Result, projAtt, viewspkg, scope, viewScope)
+			found := false
+			for _, rt := range viewedRTs {
+				if rt.Type.ID() == vrt.Type.ID() {
+					found = true
+					break
+				}
+			}
+			if !found {
+				viewedRTs = append(viewedRTs, vrt)
+			}
+			m.ViewedResult = vrt
+			seenViewed[vrt.Name+"::"+view] = vrt
 		}
 	}
 
diff --git a/dsl/validation.go b/dsl/validation.go
index 5b24e4e3..0647e666 100644
--- a/dsl/validation.go
+++ b/dsl/validation.go
@@ -458,6 +458,12 @@ func Required(names ...string) {
 			at.Validation = &expr.ValidationExpr{}
 		}
 		at.Validation.AddRequired(names...)
+		if ut, ok := at.Type.(expr.UserType); ok {
+			if ut.Attribute().Validation == nil {
+				ut.Attribute().Validation = &expr.ValidationExpr{}
+			}
+			ut.Attribute().Validation.AddRequired(names...)
+		}
 	}
 }
 


Test patch:
diff --git a/dsl/validation_test.go b/dsl/validation_test.go
index 5c19de00..d2875417 100644
--- a/dsl/validation_test.go
+++ b/dsl/validation_test.go
@@ -44,3 +44,40 @@ func TestFormat(t *testing.T) {
 		}
 	}
 }
+
+func TestRequired(t *testing.T) {
+	att := &expr.AttributeExpr{
+		Type: &expr.UserTypeExpr{
+			AttributeExpr: &expr.AttributeExpr{
+				Type: &expr.Object{
+					{"foo", &expr.AttributeExpr{Type: String}},
+					{"bar", &expr.AttributeExpr{Type: String}},
+				},
+			},
+			TypeName: "Foo",
+		},
+	}
+	eval.Context = &eval.DSLContext{}
+	eval.Execute(func() { Required("foo") }, att)
+	if eval.Context.Errors != nil {
+		t.Errorf("Required failed unexpectedly with %s", eval.Context.Errors)
+		return
+	}
+	if len(att.Validation.Required) == 0 {
+		t.Errorf("Required not set on %+v", att)
+		return
+	}
+	if att.Validation.Required[0] != "foo" {
+		t.Errorf("Required invalid on %+v, expected foo, got %+v", att, att.Validation.Required)
+	}
+	uattr := att.Type.(*expr.UserTypeExpr).AttributeExpr
+	if uattr.Validation == nil {
+		t.Fatalf("Required not set on %+v", uattr)
+	}
+	if len(uattr.Validation.Required) == 0 {
+		t.Errorf("Required not set on %+v, got %+v", uattr, uattr.Validation.Required)
+	}
+	if uattr.Validation.Required[0] != "foo" {
+		t.Errorf("Required invalid on %+v, expected foo, got %+v", uattr, uattr.Validation.Required)
+	}
+}