Skip to content

Commit

Permalink
Generate validation code for required attributes in inline struct htt…
Browse files Browse the repository at this point in the history
…p bodies (#3580)

* Split test cases for Required

* Generate validation code for required attributes

in inline struct http bodies.
  • Loading branch information
tchssk authored Sep 5, 2024
1 parent 381d088 commit 8aed3dd
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 7 deletions.
2 changes: 2 additions & 0 deletions http/codegen/server_decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ func TestDecode(t *testing.T) {
{"decode-body-user-nested", testdata.PayloadBodyNestedUserDSL, testdata.PayloadBodyNestedUserDecodeCode},
{"decode-body-user-validate", testdata.PayloadBodyUserValidateDSL, testdata.PayloadBodyUserValidateDecodeCode},
{"decode-body-object", testdata.PayloadBodyObjectDSL, testdata.PayloadBodyObjectDecodeCode},
{"decode-body-object-required", testdata.PayloadBodyObjectRequiredDSL, testdata.PayloadBodyObjectRequiredDecodeCode},
{"decode-body-object-validate", testdata.PayloadBodyObjectValidateDSL, testdata.PayloadBodyObjectValidateDecodeCode},
{"decode-body-union", testdata.PayloadBodyUnionDSL, testdata.PayloadBodyUnionDecodeCode},
{"decode-body-union-validate", testdata.PayloadBodyUnionValidateDSL, testdata.PayloadBodyUnionValidateDecodeCode},
Expand All @@ -170,6 +171,7 @@ func TestDecode(t *testing.T) {
{"decode-body-primitive-array-string-validate", testdata.PayloadBodyPrimitiveArrayStringValidateDSL, testdata.PayloadBodyPrimitiveArrayStringValidateDecodeCode},
{"decode-body-primitive-array-bool-validate", testdata.PayloadBodyPrimitiveArrayBoolValidateDSL, testdata.PayloadBodyPrimitiveArrayBoolValidateDecodeCode},

{"decode-body-primitive-array-user-required", testdata.PayloadBodyPrimitiveArrayUserRequiredDSL, testdata.PayloadBodyPrimitiveArrayUserRequiredDecodeCode},
{"decode-body-primitive-array-user-validate", testdata.PayloadBodyPrimitiveArrayUserValidateDSL, testdata.PayloadBodyPrimitiveArrayUserValidateDecodeCode},
{"decode-body-primitive-field-array-user", testdata.PayloadBodyPrimitiveFieldArrayUserDSL, testdata.PayloadBodyPrimitiveFieldArrayUserDecodeCode},
{"decode-body-extend-primitive-field-array-user", testdata.PayloadExtendBodyPrimitiveFieldArrayUserDSL, testdata.PayloadBodyPrimitiveFieldArrayUserDecodeCode},
Expand Down
5 changes: 3 additions & 2 deletions http/codegen/service_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,9 @@ func buildRequestBodyType(body, att *expr.AttributeExpr, e *expr.HTTPEndpointExp
}
}
} else {
// Generate validation code first because inline struct validation is removed.
ctx := codegen.NewAttributeContext(!expr.IsPrimitive(body.Type), false, !svr, "", sd.Scope)
validateRef = codegen.ValidationCode(body, nil, ctx, true, expr.IsAlias(body.Type), false, "body")
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
Expand All @@ -2000,8 +2003,6 @@ func buildRequestBodyType(body, att *expr.AttributeExpr, e *expr.HTTPEndpointExp
body.Validation = nil
}
varname = sd.Scope.GoTypeRef(body)
ctx := codegen.NewAttributeContext(false, false, !svr, "", sd.Scope)
validateRef = codegen.ValidationCode(body, nil, ctx, true, expr.IsAlias(body.Type), false, "body")
desc = body.Description
}
var init *InitData
Expand Down
77 changes: 77 additions & 0 deletions http/codegen/testdata/payload_decode_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3917,6 +3917,40 @@ func DecodeMethodBodyObjectRequest(mux goahttp.Muxer, decoder func(*http.Request
}
`

var PayloadBodyObjectRequiredDecodeCode = `// DecodeMethodBodyObjectRequiredRequest returns a decoder for requests sent to
// the ServiceBodyObjectRequired MethodBodyObjectRequired endpoint.
func DecodeMethodBodyObjectRequiredRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (any, error) {
return func(r *http.Request) (any, 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()
}
var gerr *goa.ServiceError
if errors.As(err, &gerr) {
return nil, gerr
}
return nil, goa.DecodePayloadError(err.Error())
}
if body.B == nil {
err = goa.MergeErrors(err, goa.MissingFieldError("b", "body"))
}
if err != nil {
return nil, err
}
payload := NewMethodBodyObjectRequiredPayload(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) (any, error) {
Expand All @@ -3938,6 +3972,12 @@ func DecodeMethodBodyObjectValidateRequest(mux goahttp.Muxer, decoder func(*http
}
return nil, goa.DecodePayloadError(err.Error())
}
if body.B != nil {
err = goa.MergeErrors(err, goa.ValidatePattern("body.b", *body.B, "pattern"))
}
if err != nil {
return nil, err
}
payload := NewMethodBodyObjectValidatePayload(body)
return payload, nil
Expand Down Expand Up @@ -4440,6 +4480,43 @@ func DecodeMethodBodyPrimitiveArrayBoolValidateRequest(mux goahttp.Muxer, decode
}
`

var PayloadBodyPrimitiveArrayUserRequiredDecodeCode = `// DecodeMethodBodyPrimitiveArrayUserRequiredRequest returns a decoder for
// requests sent to the ServiceBodyPrimitiveArrayUserRequired
// MethodBodyPrimitiveArrayUserRequired endpoint.
func DecodeMethodBodyPrimitiveArrayUserRequiredRequest(mux goahttp.Muxer, decoder func(*http.Request) goahttp.Decoder) func(*http.Request) (any, error) {
return func(r *http.Request) (any, error) {
var (
body []*PayloadTypeRequestBody
err error
)
err = decoder(r).Decode(&body)
if err != nil {
if err == io.EOF {
return nil, goa.MissingPayloadError()
}
var gerr *goa.ServiceError
if errors.As(err, &gerr) {
return nil, gerr
}
return nil, goa.DecodePayloadError(err.Error())
}
for _, e := range body {
if e != nil {
if err2 := ValidatePayloadTypeRequestBody(e); err2 != nil {
err = goa.MergeErrors(err, err2)
}
}
}
if err != nil {
return nil, err
}
payload := NewMethodBodyPrimitiveArrayUserRequiredPayloadType(body)
return payload, nil
}
}
`

var PayloadBodyPrimitiveArrayUserValidateDecodeCode = `// DecodeMethodBodyPrimitiveArrayUserValidateRequest returns a decoder for
// requests sent to the ServiceBodyPrimitiveArrayUserValidate
// MethodBodyPrimitiveArrayUserValidate endpoint.
Expand Down
41 changes: 36 additions & 5 deletions http/codegen/testdata/payload_dsls.go
Original file line number Diff line number Diff line change
Expand Up @@ -1947,7 +1947,6 @@ var PayloadBodyStringValidateDSL = func() {
Attribute("b", String, func() {
Pattern("pattern")
})
Required("b")
})
HTTP(func() {
POST("/")
Expand Down Expand Up @@ -2039,9 +2038,9 @@ var PayloadBodyObjectDSL = func() {
})
}

var PayloadBodyObjectValidateDSL = func() {
Service("ServiceBodyObjectValidate", func() {
Method("MethodBodyObjectValidate", func() {
var PayloadBodyObjectRequiredDSL = func() {
Service("ServiceBodyObjectRequired", func() {
Method("MethodBodyObjectRequired", func() {
Payload(func() {
Attribute("b", String)
Required("b")
Expand All @@ -2057,6 +2056,24 @@ var PayloadBodyObjectValidateDSL = func() {
})
}

var PayloadBodyObjectValidateDSL = func() {
Service("ServiceBodyObjectValidate", func() {
Method("MethodBodyObjectValidate", func() {
Payload(func() {
Attribute("b", String, func() {
Pattern("pattern")
})
})
HTTP(func() {
POST("/")
Body(func() {
Attribute("b", String)
})
})
})
})
}

var PayloadBodyUnionDSL = func() {
var Union = Type("Union", func() {
OneOf("Values", func() {
Expand Down Expand Up @@ -2342,12 +2359,26 @@ var PayloadBodyPrimitiveArrayBoolValidateDSL = func() {
})
}

var PayloadBodyPrimitiveArrayUserRequiredDSL = func() {
var PayloadType = Type("PayloadType", func() {
Attribute("a", String)
Required("a")
})
Service("ServiceBodyPrimitiveArrayUserRequired", func() {
Method("MethodBodyPrimitiveArrayUserRequired", func() {
Payload(ArrayOf(PayloadType))
HTTP(func() {
POST("/")
})
})
})
}

var PayloadBodyPrimitiveArrayUserValidateDSL = func() {
var PayloadType = Type("PayloadType", func() {
Attribute("a", String, func() {
Pattern("pattern")
})
Required("a")
})
Service("ServiceBodyPrimitiveArrayUserValidate", func() {
Method("MethodBodyPrimitiveArrayUserValidate", func() {
Expand Down

0 comments on commit 8aed3dd

Please sign in to comment.