diff --git a/_integration_tests/integration_test.go b/_integration_tests/integration_test.go index c461d343..c58ecd7b 100644 --- a/_integration_tests/integration_test.go +++ b/_integration_tests/integration_test.go @@ -120,6 +120,16 @@ type Multimedialist struct { } } +func TestIssue161(t *testing.T) { + defer cleanup("./issue161/*") + if err := goagen("./issue161", "bootstrap", "-d", "github.com/shogo82148/goa-v1/_integration_tests/issue161/design"); err != nil { + t.Error(err.Error()) + } + if err := gobuild("./issue161"); err != nil { + t.Error(err.Error()) + } +} + func goagen(dir, command string, args ...string) error { pkg, err := build.Import("github.com/shogo82148/goa-v1/goagen", "", 0) if err != nil { diff --git a/_integration_tests/issue161/design/design.go b/_integration_tests/issue161/design/design.go new file mode 100644 index 00000000..464b2e93 --- /dev/null +++ b/_integration_tests/issue161/design/design.go @@ -0,0 +1,23 @@ +package design + +import ( + . "github.com/shogo82148/goa-v1/design" + . "github.com/shogo82148/goa-v1/design/apidsl" +) + +var _ = API("media", func() { + Title("An API exercising the DefaultMedia definition") + Host("localhost:8080") + Scheme("http") +}) + +var Author = Type("Author", func() { + Attribute("name", String) + Attribute("country", String) + Required("name") +}) + +var Book = Type("Book", func() { + Attribute("title", String) + Attribute("author", Author) +}) diff --git a/goagen/codegen/validation.go b/goagen/codegen/validation.go index aee351bb..fef295bb 100644 --- a/goagen/codegen/validation.go +++ b/goagen/codegen/validation.go @@ -2,7 +2,6 @@ package codegen import ( "bytes" - "errors" "fmt" "math" "strings" @@ -220,44 +219,11 @@ func (v *Validator) recurse(att *design.AttributeDefinition, nonzero, required, func (v *Validator) recurseAttribute(att, catt *design.AttributeDefinition, n, target, context string, depth int, private bool) string { var validation string - if ds, ok := catt.Type.(design.DataStructure); ok { - // We need to check empirically whether there are validations to be - // generated, we can't just generate and check whether something was - // generated to avoid infinite recursions. - hasValidations := false - done := errors.New("done") - ds.Walk(func(a *design.AttributeDefinition) error { - if a.Validation != nil { - if private { - hasValidations = true - return done - } - // For public data structures there is a case where - // there is validation but no actual validation - // code: if the validation is a required validation - // that applies to attributes that cannot be nil or - // empty string i.e. primitive types other than - // string. - if !a.Validation.HasRequiredOnly() { - hasValidations = true - return done - } - for _, name := range a.Validation.Required { - att := a.Type.ToObject()[name] - if att != nil && (!att.Type.IsPrimitive() || att.Type.Kind() == design.StringKind) { - hasValidations = true - return done - } - } - } - return nil + if _, ok := catt.Type.(design.DataStructure); ok { + validation = RunTemplate(v.userValT, map[string]interface{}{ + "depth": depth, + "target": fmt.Sprintf("%s.%s", target, GoifyAtt(catt, n, true)), }) - if hasValidations { - validation = RunTemplate(v.userValT, map[string]interface{}{ - "depth": depth, - "target": fmt.Sprintf("%s.%s", target, GoifyAtt(catt, n, true)), - }) - } } else { dp := depth if catt.Type.IsObject() { diff --git a/goagen/codegen/validation_test.go b/goagen/codegen/validation_test.go index 9339f9c9..b279ec13 100644 --- a/goagen/codegen/validation_test.go +++ b/goagen/codegen/validation_test.go @@ -442,7 +442,16 @@ const ( utCode = ` if val.Foo == nil { err = goa.MergeErrors(err, goa.MissingAttributeError(` + "`context`" + `, "foo")) + } + if val.Foo2 != nil { + if err2 := val.Foo2.Validate(); err2 != nil { + err = goa.MergeErrors(err, err2) + } }` - utRequiredCode = `` + utRequiredCode = ` if val.Foo2 != nil { + if err2 := val.Foo2.Validate(); err2 != nil { + err = goa.MergeErrors(err, err2) + } + }` ) diff --git a/goagen/gen_app/writers.go b/goagen/gen_app/writers.go index 4aca9aef..49f035f8 100644 --- a/goagen/gen_app/writers.go +++ b/goagen/gen_app/writers.go @@ -891,23 +891,24 @@ func {{ .Name }}Href({{ if .CanonicalParams }}{{ join .CanonicalParams ", " }} i // Identifier: {{ .Identifier }}{{ $typeName := gotypename . .AllRequired 0 false }} type {{ $typeName }} {{ gotypedef . 0 true false }} -{{ $validation := validationCode .AttributeDefinition false false false "mt" "response" 1 false }}{{ if $validation }}// Validate validates the {{$typeName}} media type instance. +{{ $validation := validationCode .AttributeDefinition false false false "mt" "response" 1 false -}} +// Validate validates the {{$typeName}} media type instance. func (mt {{ gotyperef . .AllRequired 0 false }}) Validate() (err error) { {{ $validation }} return } -{{ end }} ` // mediaTypeLinkT generates the code for a media type link. // template input: MediaTypeLinkTemplateData mediaTypeLinkT = `// {{ gotypedesc . true }}{{ $typeName := gotypename . .AllRequired 0 false }} type {{ $typeName }} {{ gotypedef . 0 true false }} -{{ $validation := validationCode .AttributeDefinition false false false "ut" "response" 1 false }}{{ if $validation }}// Validate validates the {{$typeName}} type instance. +{{ $validation := validationCode .AttributeDefinition false false false "ut" "response" 1 false -}} +// Validate validates the {{$typeName}} type instance. func (ut {{ gotyperef . .AllRequired 0 false }}) Validate() (err error) { {{ $validation }} return -}{{ end }} +} ` // userTypeT generates the code for a user type. @@ -918,11 +919,12 @@ type {{ $privateTypeName }} {{ gotypedef . 0 true true }} func (ut {{ gotyperef . .AllRequired 0 true }}) Finalize() { {{ $assignment }} }{{ end }} -{{ $validation := validationCode .AttributeDefinition false false false "ut" "request" 1 true }}{{ if $validation }}// Validate validates the {{$privateTypeName}} type instance. +{{ $validation := validationCode .AttributeDefinition false false false "ut" "request" 1 true -}} +// Validate validates the {{$privateTypeName}} type instance. func (ut {{ gotyperef . .AllRequired 0 true }}) Validate() (err error) { {{ $validation }} return -}{{ end }} +} {{ $typeName := gotypename . .AllRequired 0 false }} // Publicize creates {{ $typeName }} from {{ $privateTypeName }} func (ut {{ gotyperef . .AllRequired 0 true }}) Publicize() {{ gotyperef . .AllRequired 0 false }} { @@ -933,11 +935,12 @@ func (ut {{ gotyperef . .AllRequired 0 true }}) Publicize() {{ gotyperef . .AllR // {{ gotypedesc . true }} type {{ $typeName }} {{ gotypedef . 0 true false }} -{{ $validation := validationCode .AttributeDefinition false false false "ut" "type" 1 false }}{{ if $validation }}// Validate validates the {{$typeName}} type instance. +{{ $validation := validationCode .AttributeDefinition false false false "ut" "type" 1 false }} +// Validate validates the {{$typeName}} type instance. func (ut {{ gotyperef . .AllRequired 0 false }}) Validate() (err error) { {{ $validation }} return -}{{ end }} +} ` // securitySchemesT generates the code for the security module. diff --git a/goagen/gen_app/writers_test.go b/goagen/gen_app/writers_test.go index dcfcedd0..e1b4518e 100644 --- a/goagen/gen_app/writers_test.go +++ b/goagen/gen_app/writers_test.go @@ -2710,7 +2710,11 @@ type simplePayload struct { Name *string ` + "`" + `form:"name,omitempty" json:"name,omitempty" yaml:"name,omitempty" xml:"name,omitempty"` + "`" + ` } +// Validate validates the simplePayload type instance. +func (ut *simplePayload) Validate() (err error) { + return +} // Publicize creates SimplePayload from simplePayload func (ut *simplePayload) Publicize() *SimplePayload { @@ -2725,6 +2729,12 @@ func (ut *simplePayload) Publicize() *SimplePayload { type SimplePayload struct { Name *string ` + "`" + `form:"name,omitempty" json:"name,omitempty" yaml:"name,omitempty" xml:"name,omitempty"` + "`" + ` } + +// Validate validates the SimplePayload type instance. +func (ut *SimplePayload) Validate() (err error) { + + return +} ` userTypeIncludingHash = `// complexPayload user type. @@ -2733,7 +2743,11 @@ type complexPayload struct { Name *string ` + "`" + `form:"name,omitempty" json:"name,omitempty" yaml:"name,omitempty" xml:"name,omitempty"` + "`" + ` } +// Validate validates the complexPayload type instance. +func (ut *complexPayload) Validate() (err error) { + return +} // Publicize creates ComplexPayload from complexPayload func (ut *complexPayload) Publicize() *ComplexPayload { @@ -2760,5 +2774,11 @@ type ComplexPayload struct { Misc map[int]*MiscPayload ` + "`" + `form:"misc,omitempty" json:"misc,omitempty" yaml:"misc,omitempty" xml:"misc,omitempty"` + "`" + ` Name *string ` + "`" + `form:"name,omitempty" json:"name,omitempty" yaml:"name,omitempty" xml:"name,omitempty"` + "`" + ` } + +// Validate validates the ComplexPayload type instance. +func (ut *ComplexPayload) Validate() (err error) { + + return +} ` )