diff --git a/codegen/service/service_data.go b/codegen/service/service_data.go index a00e30ada1..5417308892 100644 --- a/codegen/service/service_data.go +++ b/codegen/service/service_data.go @@ -1189,6 +1189,9 @@ func BuildSchemeData(s *expr.SchemeExpr, m *expr.MethodExpr) *SchemeData { // just result types - because user types make contain result types and thus may // need to be marshalled in different ways depending on the view being used. func collectProjectedTypes(projected, att *expr.AttributeExpr, viewspkg string, scope, viewScope *codegen.NameScope, seen map[string]*ProjectedTypeData) (data []*ProjectedTypeData, umeths []*UnionValueMethodData) { + if !hasResultType(att) { + return + } collect := func(projected, att *expr.AttributeExpr) ([]*ProjectedTypeData, []*UnionValueMethodData) { return collectProjectedTypes(projected, att, viewspkg, scope, viewScope, seen) } @@ -1253,6 +1256,44 @@ func collectProjectedTypes(projected, att *expr.AttributeExpr, viewspkg string, return } +// hasResultType returns true if the given attribute has a result type recursively. +func hasResultType(att *expr.AttributeExpr, seens ...map[string]struct{}) bool { + if _, ok := att.Type.(*expr.ResultTypeExpr); ok { + return true + } + var seen map[string]struct{} + if len(seens) > 0 { + seen = seens[0] + } else { + seen = make(map[string]struct{}) + } + switch a := att.Type.(type) { + case expr.UserType: + if _, ok := seen[a.ID()]; ok { + return false + } + seen[a.ID()] = struct{}{} + return hasResultType(a.Attribute(), seen) + case *expr.Array: + return hasResultType(a.ElemType, seen) + case *expr.Map: + return hasResultType(a.KeyType, seen) || hasResultType(a.ElemType, seen) + case *expr.Object: + for _, nat := range *a { + if hasResultType(nat.Attribute, seen) { + return true + } + } + case *expr.Union: + for _, nat := range a.Values { + if hasResultType(nat.Attribute, seen) { + return true + } + } + } + return false +} + // buildProjectedType builds projected type for the given user type. // // viewspkg is the name of the views package diff --git a/codegen/service/testdata/service_code.go b/codegen/service/testdata/service_code.go index f6a9b5bf9a..8dc4fd2308 100644 --- a/codegen/service/testdata/service_code.go +++ b/codegen/service/testdata/service_code.go @@ -1537,15 +1537,15 @@ func newResultOneof(vres *resultwithoneoftypeviews.ResultOneofView) *ResultOneof res := &ResultOneof{} if vres.Result != nil { switch actual := vres.Result.(type) { - case *resultwithoneoftypeviews.TView: + case *resultwithoneoftypeviews.T: obj := &T{ Message: actual.Message, } res.Result = obj - case *resultwithoneoftypeviews.UView: + case *resultwithoneoftypeviews.U: obj := &U{} if actual.Item != nil { - obj.(*U).Item = transformResultwithoneoftypeviewsItemViewToItem(actual.Item) + obj.(*U).Item = transformResultwithoneoftypeviewsItemToItem(actual.Item) } res.Result = obj } @@ -1560,14 +1560,14 @@ func newResultOneofView(res *ResultOneof) *resultwithoneoftypeviews.ResultOneofV if res.Result != nil { switch actual := res.Result.(type) { case *T: - obj := &resultwithoneoftypeviews.TView{ + obj := &resultwithoneoftypeviews.T{ Message: actual.Message, } vres.Result = obj case *U: - obj := &resultwithoneoftypeviews.UView{} + obj := &resultwithoneoftypeviews.U{} if actual.Item != nil { - obj.(*resultwithoneoftypeviews.UView).Item = transformItemToResultwithoneoftypeviewsItemView(actual.Item) + obj.(*resultwithoneoftypeviews.U).Item = transformItemToResultwithoneoftypeviewsItem(actual.Item) } vres.Result = obj } @@ -1575,9 +1575,9 @@ func newResultOneofView(res *ResultOneof) *resultwithoneoftypeviews.ResultOneofV return vres } -// transformResultwithoneoftypeviewsTViewToT builds a value of type *T from a -// value of type *resultwithoneoftypeviews.TView. -func transformResultwithoneoftypeviewsTViewToT(v *resultwithoneoftypeviews.TView) *T { +// transformResultwithoneoftypeviewsTToT builds a value of type *T from a value +// of type *resultwithoneoftypeviews.T. +func transformResultwithoneoftypeviewsTToT(v *resultwithoneoftypeviews.T) *T { if v == nil { return nil } @@ -1588,23 +1588,23 @@ func transformResultwithoneoftypeviewsTViewToT(v *resultwithoneoftypeviews.TView return res } -// transformResultwithoneoftypeviewsUViewToU builds a value of type *U from a -// value of type *resultwithoneoftypeviews.UView. -func transformResultwithoneoftypeviewsUViewToU(v *resultwithoneoftypeviews.UView) *U { +// transformResultwithoneoftypeviewsUToU builds a value of type *U from a value +// of type *resultwithoneoftypeviews.U. +func transformResultwithoneoftypeviewsUToU(v *resultwithoneoftypeviews.U) *U { if v == nil { return nil } res := &U{} if v.Item != nil { - res.Item = transformResultwithoneoftypeviewsItemViewToItem(v.Item) + res.Item = transformResultwithoneoftypeviewsItemToItem(v.Item) } return res } -// transformResultwithoneoftypeviewsItemViewToItem builds a value of type *Item -// from a value of type *resultwithoneoftypeviews.ItemView. -func transformResultwithoneoftypeviewsItemViewToItem(v *resultwithoneoftypeviews.ItemView) *Item { +// transformResultwithoneoftypeviewsItemToItem builds a value of type *Item +// from a value of type *resultwithoneoftypeviews.Item. +func transformResultwithoneoftypeviewsItemToItem(v *resultwithoneoftypeviews.Item) *Item { if v == nil { return nil } @@ -1615,40 +1615,40 @@ func transformResultwithoneoftypeviewsItemViewToItem(v *resultwithoneoftypeviews return res } -// transformTToResultwithoneoftypeviewsTView builds a value of type -// *resultwithoneoftypeviews.TView from a value of type *T. -func transformTToResultwithoneoftypeviewsTView(v *T) *resultwithoneoftypeviews.TView { +// transformTToResultwithoneoftypeviewsT builds a value of type +// *resultwithoneoftypeviews.T from a value of type *T. +func transformTToResultwithoneoftypeviewsT(v *T) *resultwithoneoftypeviews.T { if v == nil { return nil } - res := &resultwithoneoftypeviews.TView{ + res := &resultwithoneoftypeviews.T{ Message: v.Message, } return res } -// transformUToResultwithoneoftypeviewsUView builds a value of type -// *resultwithoneoftypeviews.UView from a value of type *U. -func transformUToResultwithoneoftypeviewsUView(v *U) *resultwithoneoftypeviews.UView { +// transformUToResultwithoneoftypeviewsU builds a value of type +// *resultwithoneoftypeviews.U from a value of type *U. +func transformUToResultwithoneoftypeviewsU(v *U) *resultwithoneoftypeviews.U { if v == nil { return nil } - res := &resultwithoneoftypeviews.UView{} + res := &resultwithoneoftypeviews.U{} if v.Item != nil { - res.Item = transformItemToResultwithoneoftypeviewsItemView(v.Item) + res.Item = transformItemToResultwithoneoftypeviewsItem(v.Item) } return res } -// transformItemToResultwithoneoftypeviewsItemView builds a value of type -// *resultwithoneoftypeviews.ItemView from a value of type *Item. -func transformItemToResultwithoneoftypeviewsItemView(v *Item) *resultwithoneoftypeviews.ItemView { +// transformItemToResultwithoneoftypeviewsItem builds a value of type +// *resultwithoneoftypeviews.Item from a value of type *Item. +func transformItemToResultwithoneoftypeviewsItem(v *Item) *resultwithoneoftypeviews.Item { if v == nil { return nil } - res := &resultwithoneoftypeviews.ItemView{ + res := &resultwithoneoftypeviews.Item{ A: v.A, } diff --git a/codegen/service/testdata/views_code.go b/codegen/service/testdata/views_code.go index 6c2c0ff38f..8b0767a02e 100644 --- a/codegen/service/testdata/views_code.go +++ b/codegen/service/testdata/views_code.go @@ -175,15 +175,10 @@ type ResultType struct { // ResultTypeView is a type that runs validations on a projected type. type ResultTypeView struct { - A *UserTypeView + A *UserType B *string } -// UserTypeView is a type that runs validations on a projected type. -type UserTypeView struct { - A *string -} - var ( // ResultTypeMap is a map indexing the attribute names of ResultType by view // name. @@ -229,12 +224,6 @@ func ValidateResultTypeViewTiny(result *ResultTypeView) (err error) { } return } - -// ValidateUserTypeView runs the validations defined on UserTypeView. -func ValidateUserTypeView(result *UserTypeView) (err error) { - - return -} ` const ResultWithResultTypeCode = `// RT is the viewed result type that is projected based on a view. @@ -255,19 +244,14 @@ type RTView struct { // RT2View is a type that runs validations on a projected type. type RT2View struct { C *string - D *UserTypeView + D *UserType E *string } -// UserTypeView is a type that runs validations on a projected type. -type UserTypeView struct { - P *string -} - // RT3View is a type that runs validations on a projected type. type RT3View struct { X []string - Y map[int]*UserTypeView + Y map[int]*UserType Z *string } @@ -391,12 +375,6 @@ func ValidateRT2ViewTiny(result *RT2View) (err error) { return } -// ValidateUserTypeView runs the validations defined on UserTypeView. -func ValidateUserTypeView(result *UserTypeView) (err error) { - - return -} - // ValidateRT3View runs the validations defined on RT3View using the "default" // view. func ValidateRT3View(result *RT3View) (err error) { @@ -763,12 +741,9 @@ type Result struct { // ResultView is a type that runs validations on a projected type. type ResultView struct { - T []UserTypeView + T []UserType } -// UserTypeView is a type that runs validations on a projected type. -type UserTypeView string - var ( // ResultMap is a map indexing the attribute names of Result by view name. ResultMap = map[string][]string{ @@ -799,14 +774,6 @@ func ValidateResultView(result *ResultView) (err error) { } return } - -// ValidateUserTypeView runs the validations defined on UserTypeView. -func ValidateUserTypeView(result UserTypeView) (err error) { - if !(string(result) == "a" || string(result) == "b") { - err = goa.MergeErrors(err, goa.InvalidEnumValueError("result", string(result), []any{"a", "b"})) - } - return -} ` const ResultWithPkgPathCode = `// RT is the viewed result type that is projected based on a view. @@ -819,12 +786,7 @@ type RT struct { // RTView is a type that runs validations on a projected type. type RTView struct { - A *UserTypeView -} - -// UserTypeView is a type that runs validations on a projected type. -type UserTypeView struct { - A *string + A *UserType } var ( @@ -853,10 +815,4 @@ func ValidateRTView(result *RTView) (err error) { return } - -// ValidateUserTypeView runs the validations defined on UserTypeView. -func ValidateUserTypeView(result *UserTypeView) (err error) { - - return -} ` diff --git a/http/codegen/client_body_types_test.go b/http/codegen/client_body_types_test.go index 1abad7d8f1..f5519bff59 100644 --- a/http/codegen/client_body_types_test.go +++ b/http/codegen/client_body_types_test.go @@ -236,7 +236,7 @@ const ExplicitBodyUserResultMultipleViewsInitCode = `// NewMethodExplicitBodyUse // "MethodExplicitBodyUserResultMultipleView" endpoint result from a HTTP "OK" // response. func NewMethodExplicitBodyUserResultMultipleViewResulttypemultipleviewsOK(body *MethodExplicitBodyUserResultMultipleViewResponseBody, c *string) *serviceexplicitbodyuserresultmultipleviewviews.ResulttypemultipleviewsView { - v := &serviceexplicitbodyuserresultmultipleviewviews.UserTypeView{ + v := &serviceexplicitbodyuserresultmultipleviewviews.UserType{ X: body.X, Y: body.Y, } @@ -256,7 +256,7 @@ const ExplicitBodyObjectInitCode = `// NewMethodExplicitBodyUserResultObjectResu func NewMethodExplicitBodyUserResultObjectResulttypeOK(body *MethodExplicitBodyUserResultObjectResponseBody, c *string, b *string) *serviceexplicitbodyuserresultobjectviews.ResulttypeView { v := &serviceexplicitbodyuserresultobjectviews.ResulttypeView{} if body.A != nil { - v.A = unmarshalUserTypeResponseBodyToServiceexplicitbodyuserresultobjectviewsUserTypeView(body.A) + v.A = unmarshalUserTypeResponseBodyToServiceexplicitbodyuserresultobjectviewsUserType(body.A) } v.C = c v.B = b @@ -272,7 +272,7 @@ const ExplicitBodyObjectViewsInitCode = `// NewMethodExplicitBodyUserResultObjec func NewMethodExplicitBodyUserResultObjectMultipleViewResulttypemultipleviewsOK(body *MethodExplicitBodyUserResultObjectMultipleViewResponseBody, c *string) *serviceexplicitbodyuserresultobjectmultipleviewviews.ResulttypemultipleviewsView { v := &serviceexplicitbodyuserresultobjectmultipleviewviews.ResulttypemultipleviewsView{} if body.A != nil { - v.A = unmarshalUserTypeResponseBodyToServiceexplicitbodyuserresultobjectmultipleviewviewsUserTypeView(body.A) + v.A = unmarshalUserTypeResponseBodyToServiceexplicitbodyuserresultobjectmultipleviewviewsUserType(body.A) } v.C = c