Repository: goadesign/goa (go)

Issue:
When add String value to Required attribute in body in html generated code check if string == nil and this not acceptable in go cause string have zero value 
Design :

```  
var _ = Service("foo", func() {

	Method("bar", func() {

		Payload(func() {
			Field(1, "user-id", String, "User Id")
			Required("user-id")
		})

               HTTP(func() {
			POST("/)
			Body(func() {
				Attribute("user-id", String, "User Id")
				Required( "user-id")
			})
			Response(StatusOK)
		})

})
```


the generated code :  
```	
if body.UserID == nil {
   err = goa.MergeErrors(err, goa.MissingFieldError("user-id", "body"))
}
```



Pull request:
Properly handle explicit body attribute
When corresponding payload attribute isn't required.
Fix #2491

Patch:
diff --git a/codegen/cli/cli.go b/codegen/cli/cli.go
index 4817a4c1..0ef6da69 100644
--- a/codegen/cli/cli.go
+++ b/codegen/cli/cli.go
@@ -130,6 +130,9 @@ type (
 		// ReturnTypeAttribute if non-empty returns an attribute in the payload
 		// type that describes the shape of the method payload.
 		ReturnTypeAttribute string
+		// ReturnTypeAttributePointer is true if the return type attribute
+		// generated struct field holds a pointer
+		ReturnTypeAttributePointer bool
 		// ReturnIsStruct if true indicates that the method payload is an object.
 		ReturnIsStruct bool
 		// ReturnTypeName is the fully-qualified name of the payload.
@@ -712,7 +715,7 @@ func {{ .Name }}({{ range .FormalParams }}{{ . }} string, {{ end }}) ({{ .Result
 		{{ .Code }}
 		{{- if .ReturnTypeAttribute }}
 			res := &{{ .ReturnTypeName }}{
-				{{ .ReturnTypeAttribute }}: v,
+				{{ .ReturnTypeAttribute }}: {{ if .ReturnTypeAttributePointer }}&{{ end }}v,
 			}
 		{{- end }}
 	{{- end }}
diff --git a/dsl/http.go b/dsl/http.go
index 25b0e946..e373ccc0 100644
--- a/dsl/http.go
+++ b/dsl/http.go
@@ -735,6 +735,13 @@ func Body(args ...interface{}) {
 			// GeneratedTypes so that the type's DSLFunc is executed.
 			*expr.Root.GeneratedTypes = append(*expr.Root.GeneratedTypes, rt)
 		}
+		if len(args) > 1 {
+			var ok bool
+			fn, ok = args[1].(func())
+			if !ok {
+				eval.ReportError("second argument must be a function")
+			}
+		}
 	case expr.UserType:
 		attr = &expr.AttributeExpr{Type: a}
 		if len(args) > 1 {
diff --git a/dsl/result_type.go b/dsl/result_type.go
index ed8986c7..e52e493c 100644
--- a/dsl/result_type.go
+++ b/dsl/result_type.go
@@ -134,19 +134,33 @@ func TypeName(name string) {
 	}
 }
 
-// View adds a new view to a result type. A view has a name and lists attributes
-// that are rendered when the view is used to produce a response. The attribute
-// names must appear in the result type expression. If an attribute is itself a
-// result type then the view may specify which view to use when rendering the
-// attribute using the View function in the View DSL. If not specified then the
-// view named "default" is used.
+// View has two usages:
 //
-// View must appear in a ResultType expression.
+// - when used inside a ResultType DSL function it defines a view for the result
+// type. A view lists a subset of the result type attributes that are used when
+// marshalling responses.
 //
-// View accepts two arguments: the view name and its defining DSL.
+// - when used inside a Result DSL function it defines the view used to marshal
+// the result type returned by the method.
+//
+// Note that the view used to render a response can also be set dynamically by
+// the method code in which case the result function should not specify a view
+// in the design.  The attribute names listed in a view must be identical to
+// existing attributes in the result type on which the view is defined. If an
+// attribute is itself a result type then the view may specify which view to use
+// when marshaling the attribute using the View function recursively, see
+// example below. All result types must have a view called "default" which is
+// the view used to marshal results when no specific view is specified.
+//
+// View must appear in a ResultType or a Result expression.
+//
+// View accepts two arguments for the first usage: the view name and its
+// defining DSL.  View accepts a single argument for the second usage: the view
+// name used to render the result.
 //
 // Examples:
 //
+//    // MyResultType defines 2 views.
 //    var MyResultType = ResultType("application/vnd.goa.my", func() {
 //        Attributes(func() {
 //            Attribute("id", String)
@@ -170,12 +184,13 @@ func TypeName(name string) {
 //        })
 //    })
 //
-//    Result(MyResultType, func() {
-//        View("extended")
-//    })
-//
-//    Result(CollectionOf(MyResultType), func() {
-//        View("default")
+//    // MyMethod uses the extended view of MyResultType to marshal the
+//    // response.
+//    var _ = Service("MyService", func() {
+//        Method("MyMethod", func() {
+//            Result(MyResultType, func() { View("extended") })
+//            GRPC(func() {})
+//        })
 //    })
 //
 func View(name string, adsl ...func()) {
diff --git a/expr/http_endpoint.go b/expr/http_endpoint.go
index 608c936e..74b02256 100644
--- a/expr/http_endpoint.go
+++ b/expr/http_endpoint.go
@@ -394,10 +394,40 @@ func (e *HTTPEndpointExpr) Validate() error {
 
 	// Validate body attribute (required fields exist etc.)
 	if e.Body != nil {
-		verr.Merge(e.Body.Validate("HTTP endpoint payload", e))
+		verr.Merge(e.Body.Validate("HTTP body", e))
 		if e.SkipRequestBodyEncodeDecode {
 			verr.Add(e, "Cannot define a request body when using SkipRequestBodyEncodeDecode.")
 		}
+		// Make sure Body does not require attribute that are not required in
+		// payload.
+		if v := e.Body.Validation; v != nil {
+			var preqs, missing []string
+			if e.MethodExpr.Payload != nil && e.MethodExpr.Payload.Validation != nil {
+				preqs = e.MethodExpr.Payload.Validation.Required
+			}
+			for _, req := range v.Required {
+				found := false
+				for _, preq := range preqs {
+					if req == preq {
+						found = true
+						break
+					}
+				}
+				if !found {
+					missing = append(missing, req)
+				}
+			}
+			if len(missing) > 0 {
+				is := "is"
+				s := ""
+				if len(missing) > 1 {
+					is = "are"
+					s = "s"
+				}
+				verr.Add(e, "The following HTTP request body attribute%s %s required but the corresponding method payload attribute%s %s not: %s. Use 'Required' to make the attribute%s required in the method payload as well.",
+					s, is, s, is, strings.Join(missing, ", "), s)
+			}
+		}
 	}
 
 	// Validate errors
diff --git a/http/codegen/client_cli.go b/http/codegen/client_cli.go
index 9450c802..6f95622c 100644
--- a/http/codegen/client_cli.go
+++ b/http/codegen/client_cli.go
@@ -244,12 +244,13 @@ func makeFlags(e *EndpointData, args []*InitArgData, payload expr.DataType) ([]*
 	}
 
 	pInit := cli.PayloadInitData{
-		Code:                e.Payload.Request.PayloadInit.ClientCode,
-		ReturnTypeAttribute: e.Payload.Request.PayloadInit.ReturnTypeAttribute,
-		ReturnIsStruct:      e.Payload.Request.PayloadInit.ReturnIsStruct,
-		ReturnTypeName:      e.Payload.Request.PayloadInit.ReturnTypeName,
-		ReturnTypePkg:       e.Payload.Request.PayloadInit.ReturnTypePkg,
-		Args:                pInitArgs,
+		Code:                       e.Payload.Request.PayloadInit.ClientCode,
+		ReturnTypeAttribute:        e.Payload.Request.PayloadInit.ReturnTypeAttribute,
+		ReturnTypeAttributePointer: e.Payload.Request.PayloadInit.ReturnIsPrimitivePointer,
+		ReturnIsStruct:             e.Payload.Request.PayloadInit.ReturnIsStruct,
+		ReturnTypeName:             e.Payload.Request.PayloadInit.ReturnTypeName,
+		ReturnTypePkg:              e.Payload.Request.PayloadInit.ReturnTypePkg,
+		Args:                       pInitArgs,
 	}
 
 	return flags, &cli.BuildFunctionData{
diff --git a/http/codegen/server_types.go b/http/codegen/server_types.go
index 6aa0fa49..10a83991 100644
--- a/http/codegen/server_types.go
+++ b/http/codegen/server_types.go
@@ -254,7 +254,7 @@ func {{ .Name }}({{- range .ServerArgs }}{{ .Name }} {{ .TypeRef }}, {{ end }})
 	{{ .ServerCode }}
 	{{- if .ReturnTypeAttribute }}
 		res := &{{ .ReturnTypeName }}{
-			{{ .ReturnTypeAttribute }}: v,
+			{{ .ReturnTypeAttribute }}: {{ if .ReturnIsPrimitivePointer }}&{{ end }}v,
 		}
 	{{- end }}
 {{- end }}
diff --git a/http/codegen/service_data.go b/http/codegen/service_data.go
index dc015578..a58204c4 100644
--- a/http/codegen/service_data.go
+++ b/http/codegen/service_data.go
@@ -350,31 +350,30 @@ type (
 		// as the time of writing is only used for the basic auth
 		// username and password.
 		CLIArgs []*InitArgData
+		// ServerCode is the code that builds the payload from the
+		// request on the server when it contains user types.
+		ServerCode string
+		// ClientCode is the code that builds the payload or result type
+		// from the request or response state on the client when it
+		// contains user types.
+		ClientCode string
+		// ReturnTypePkg is the package where the return type is present.
+		ReturnTypePkg string
 		// ReturnTypeName is the qualified (including the package name)
 		// name of the payload, result or error type.
 		ReturnTypeName string
 		// ReturnTypeRef is the qualified (including the package name)
 		// reference to the payload, result or error type.
 		ReturnTypeRef string
-		// ReturnTypeAttribute is the name of the attribute initialized
-		// by this constructor when it only initializes one attribute
-		// (i.e. body was defined with Body("name") syntax).
+		// ReturnTypeAttribute is the name of the attribute initialized by this
+		// constructor when it only initializes one attribute (i.e. body was
+		// defined with Body("name") syntax).
 		ReturnTypeAttribute string
-		// ReturnIsStruct is true if the return type is a struct.
+		// ReturnIsStruct is true if the payload, result or error type is a struct.
 		ReturnIsStruct bool
-		// ReturnIsPrimitivePointer indicates whether the return type is
-		// a primitive pointer.
+		// ReturnIsPrimitivePointer indicates whether the payload, result or error
+		// type is a primitive pointer.
 		ReturnIsPrimitivePointer bool
-		// ReturnTypePkg is the package where the return type is
-		// present.
-		ReturnTypePkg string
-		// ServerCode is the code that builds the payload from the
-		// request on the server when it contains user types.
-		ServerCode string
-		// ClientCode is the code that builds the payload or result type
-		// from the request or response state on the client when it
-		// contains user types.
-		ClientCode string
 	}
 
 	// InitArgData represents a single constructor argument.
@@ -1275,6 +1274,7 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 			clientCode string
 			err        error
 			origin     string
+			pointer    bool
 
 			pAtt = payload
 		)
@@ -1284,6 +1284,7 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 			if o, ok := e.Body.Meta["origin:attribute"]; ok {
 				origin = o[0]
 				pAtt = expr.AsObject(payload.Type).Attribute(origin)
+				pointer = !payload.IsRequired(o[0])
 			}
 
 			var (
@@ -1318,18 +1319,19 @@ func buildPayloadData(e *expr.HTTPEndpointExpr, sd *ServiceData) *PayloadData {
 			fmt.Println(err.Error()) // TBD validate DSL so errors are not possible
 		}
 		init = &InitData{
-			Name:                name,
-			Description:         desc,
-			ServerArgs:          serverArgs,
-			ClientArgs:          clientArgs,
-			CLIArgs:             cliArgs,
-			ReturnTypeName:      svc.Scope.GoFullTypeName(payload, svc.PkgName),
-			ReturnTypeRef:       svc.Scope.GoFullTypeRef(payload, svc.PkgName),
-			ReturnIsStruct:      isObject,
-			ReturnTypeAttribute: codegen.Goify(origin, true),
-			ReturnTypePkg:       svc.PkgName,
-			ServerCode:          serverCode,
-			ClientCode:          clientCode,
+			Name:                     name,
+			Description:              desc,
+			ServerArgs:               serverArgs,
+			ClientArgs:               clientArgs,
+			CLIArgs:                  cliArgs,
+			ReturnTypeName:           svc.Scope.GoFullTypeName(payload, svc.PkgName),
+			ReturnTypeRef:            svc.Scope.GoFullTypeRef(payload, svc.PkgName),
+			ReturnIsStruct:           isObject,
+			ReturnTypeAttribute:      codegen.Goify(origin, true),
+			ReturnTypePkg:            svc.PkgName,
+			ServerCode:               serverCode,
+			ClientCode:               clientCode,
+			ReturnIsPrimitivePointer: pointer,
 		}
 	}
 	request.PayloadInit = init


Test patch:
diff --git a/expr/http_endpoint_test.go b/expr/http_endpoint_test.go
index 829c0d94..cbd2ddbe 100644
--- a/expr/http_endpoint_test.go
+++ b/expr/http_endpoint_test.go
@@ -150,6 +150,10 @@ service "Service" HTTP endpoint "Method": HTTP endpoint response body must be em
 			DSL:   testdata.EndpointHasSkipEncodeAndGRPC,
 			Error: `service "Service" HTTP endpoint "Method": Endpoint cannot use SkipRequestBodyEncodeDecode and define a gRPC transport.`,
 		},
+		"endpoint-payload-missing-required": {
+			DSL:   testdata.EndpointPayloadMissingRequired,
+			Error: `service "Service" HTTP endpoint "Method": The following HTTP request body attribute is required but the corresponding method payload attribute is not: nonreq. Use 'Required' to make the attribute required in the method payload as well.`,
+		},
 	}
 	for name, c := range cases {
 		t.Run(name, func(t *testing.T) {
diff --git a/expr/testdata/endpoint_dsls.go b/expr/testdata/endpoint_dsls.go
index 56ce7fc3..5a149855 100644
--- a/expr/testdata/endpoint_dsls.go
+++ b/expr/testdata/endpoint_dsls.go
@@ -410,6 +410,23 @@ var EndpointHasSkipEncodeAndGRPC = func() {
 	})
 }
 
+var EndpointPayloadMissingRequired = func() {
+	Service("Service", func() {
+		Method("Method", func() {
+			Payload(func() {
+				Attribute("nonreq")
+			})
+			HTTP(func() {
+				POST("/")
+				Body(func() {
+					Attribute("nonreq")
+					Required("nonreq")
+				})
+			})
+		})
+	})
+}
+
 var FinalizeEndpointBodyAsExtendedTypeDSL = func() {
 	var EntityData = Type("EntityData", func() {
 		Attribute("name", String)