Repository: goadesign/goa (go)

Issue:
Invalid indirect of type
Hi team,

I encountered an issue with using the `Required` DSL in `Payload` and `Body` in the design file. It generates the files fine but fails to compile. Received the following error:
```
gen/http/testapi/server/types.go:471:18: invalid indirect of body.name (type string)
```

```
Method("Test", func() {
		Payload(User)
                ...
		HTTP(func() {
			Body(User)
                        ......
	})

var User = func() {
	Attribute("name", ArrayOf(String, func() { Example("John") }))
	Required("name")
}
```

Pull request:
Properly handle explicit object in HTTP body
when the object defines required attributes. In this case we
still need to generate a data structure using pointers when
defining the body variable during decoding since validations
haven't run yet.

Fix #2686

Patch:
diff --git a/http/codegen/service_data.go b/http/codegen/service_data.go
index b0dca1ee..f1b191fd 100644
--- a/http/codegen/service_data.go
+++ b/http/codegen/service_data.go
@@ -1935,6 +1935,13 @@ func buildRequestBodyType(body, att *expr.AttributeExpr, e *expr.HTTPEndpointExp
 				}
 			}
 		} else {
+			if svr && expr.IsObject(body.Type) {
+				// Body is an explicit object described in the design and in
+				// this case the GoTypeRef is an inline struct definition. We
+				// want to force all attributes to be pointers because we are
+				// generating the server body type pre-validation.
+				body.Validation = nil
+			}
 			varname = sd.Scope.GoTypeRef(body)
 			ctx := codegen.NewAttributeContext(false, false, !svr, "", sd.Scope)
 			validateRef = codegen.RecursiveValidationCode(body, ctx, true, "body")


Test patch:
diff --git a/http/codegen/server_decode_test.go b/http/codegen/server_decode_test.go
index 27fc5894..4e3dffcf 100644
--- a/http/codegen/server_decode_test.go
+++ b/http/codegen/server_decode_test.go
@@ -139,6 +139,8 @@ func TestDecode(t *testing.T) {
 		{"body-user-required", testdata.PayloadBodyUserRequiredDSL, testdata.PayloadBodyUserRequiredDecodeCode},
 		{"body-user-nested", testdata.PayloadBodyNestedUserDSL, testdata.PayloadBodyNestedUserDecodeCode},
 		{"body-user-validate", testdata.PayloadBodyUserValidateDSL, testdata.PayloadBodyUserValidateDecodeCode},
+		{"body-object", testdata.PayloadBodyObjectDSL, testdata.PayloadBodyObjectDecodeCode},
+		{"body-object-validate", testdata.PayloadBodyObjectValidateDSL, testdata.PayloadBodyObjectValidateDecodeCode},
 		{"body-array-string", testdata.PayloadBodyArrayStringDSL, testdata.PayloadBodyArrayStringDecodeCode},
 		{"body-array-string-validate", testdata.PayloadBodyArrayStringValidateDSL, testdata.PayloadBodyArrayStringValidateDecodeCode},
 		{"body-array-user", testdata.PayloadBodyArrayUserDSL, testdata.PayloadBodyArrayUserDecodeCode},
diff --git a/http/codegen/testdata/payload_decode_functions.go b/http/codegen/testdata/payload_decode_functions.go
index cf054327..97ac6332 100644
--- a/http/codegen/testdata/payload_decode_functions.go
+++ b/http/codegen/testdata/payload_decode_functions.go
@@ -3742,21 +3742,70 @@ var PayloadBodyUserValidateDecodeCode = `// DecodeMethodBodyUserValidateRequest
 func DecodeMethodBodyUserValidateRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (interface{}, error) {
 	return func(r *http.Request) (interface{}, error) {
 		var (
-			body MethodBodyUserValidateRequestBody
+			body string
 			err  error
 		)
 		err = decoder(r).Decode(&body)
+		if err != nil {
+			if err == io.EOF {
+				err = nil
+			} else {
+				return nil, goa.DecodePayloadError(err.Error())
+			}
+		}
+		err = goa.MergeErrors(err, goa.ValidatePattern("body", body, "apattern"))
+		if err != nil {
+			return nil, err
+		}
+		payload := NewMethodBodyUserValidatePayloadType(body)
+
+		return payload, nil
+	}
+}
+`
+
+var PayloadBodyObjectDecodeCode = `// DecodeMethodBodyObjectRequest returns a decoder for requests sent to the
+// ServiceBodyObject MethodBodyObject endpoint.
+func DecodeMethodBodyObjectRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (interface{}, error) {
+	return func(r *http.Request) (interface{}, error) {
+		var (
+			body struct {
+				B *string ` + "`" + `form:"b" json:"b" xml:"b"` + "`" + `
+			}
+			err error
+		)
+		err = decoder(r).Decode(&body)
 		if err != nil {
 			if err == io.EOF {
 				return nil, goa.MissingPayloadError()
 			}
 			return nil, goa.DecodePayloadError(err.Error())
 		}
-		err = ValidateMethodBodyUserValidateRequestBody(&body)
+		payload := NewMethodBodyObjectPayload(body)
+
+		return payload, nil
+	}
+}
+`
+
+var PayloadBodyObjectValidateDecodeCode = `// DecodeMethodBodyObjectValidateRequest returns a decoder for requests sent to
+// the ServiceBodyObjectValidate MethodBodyObjectValidate endpoint.
+func DecodeMethodBodyObjectValidateRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (interface{}, error) {
+	return func(r *http.Request) (interface{}, error) {
+		var (
+			body struct {
+				B *string ` + "`" + `form:"b" json:"b" xml:"b"` + "`" + `
+			}
+			err error
+		)
+		err = decoder(r).Decode(&body)
 		if err != nil {
-			return nil, err
+			if err == io.EOF {
+				return nil, goa.MissingPayloadError()
+			}
+			return nil, goa.DecodePayloadError(err.Error())
 		}
-		payload := NewMethodBodyUserValidatePayloadType(&body)
+		payload := NewMethodBodyObjectValidatePayload(body)
 
 		return payload, nil
 	}
diff --git a/http/codegen/testdata/payload_dsls.go b/http/codegen/testdata/payload_dsls.go
index 954f73ed..606167da 100644
--- a/http/codegen/testdata/payload_dsls.go
+++ b/http/codegen/testdata/payload_dsls.go
@@ -1985,6 +1985,41 @@ var PayloadBodyUserValidateDSL = func() {
 			Payload(PayloadType)
 			HTTP(func() {
 				POST("/")
+				Body("a")
+			})
+		})
+	})
+}
+
+var PayloadBodyObjectDSL = func() {
+	Service("ServiceBodyObject", func() {
+		Method("MethodBodyObject", func() {
+			Payload(func() {
+				Attribute("b", String)
+			})
+			HTTP(func() {
+				POST("/")
+				Body(func() {
+					Attribute("b", String)
+				})
+			})
+		})
+	})
+}
+
+var PayloadBodyObjectValidateDSL = func() {
+	Service("ServiceBodyObjectValidate", func() {
+		Method("MethodBodyObjectValidate", func() {
+			Payload(func() {
+				Attribute("b", String)
+				Required("b")
+			})
+			HTTP(func() {
+				POST("/")
+				Body(func() {
+					Attribute("b", String)
+					Required("b")
+				})
 			})
 		})
 	})
diff --git a/http/codegen/testdata/payload_encode_functions.go b/http/codegen/testdata/payload_encode_functions.go
index 81495bd0..c5898341 100644
--- a/http/codegen/testdata/payload_encode_functions.go
+++ b/http/codegen/testdata/payload_encode_functions.go
@@ -1804,7 +1804,7 @@ func EncodeMethodBodyUserValidateRequest(encoder func(*http.Request) goahttp.Enc
 		if !ok {
 			return goahttp.ErrInvalidType("ServiceBodyUserValidate", "MethodBodyUserValidate", "*servicebodyuservalidate.PayloadType", v)
 		}
-		body := NewMethodBodyUserValidateRequestBody(p)
+		body := p.A
 		if err := encoder(req).Encode(&body); err != nil {
 			return goahttp.ErrEncodingError("ServiceBodyUserValidate", "MethodBodyUserValidate", err)
 		}