@@ -2116,9 +2116,11 @@ func (sds *ServicesData) buildRequestBodyType(body, att *expr.AttributeExpr, e *
21162116 def = goTypeDef (sd .Scope , ut .Attribute (), svr , ! svr )
21172117 desc = fmt .Sprintf ("%s is the type of the %q service %q endpoint HTTP request body." ,
21182118 varname , svc .Name , e .Name ())
2119- if svr {
2120- // generate validation code for unmarshaled type (server-side).
2121- validateDef = codegen .ValidationCode (ut .Attribute (), ut , httpctx , true , expr .IsAlias (ut ), false , "body" )
2119+ // Generate validation code for unmarshaled request bodies on the server,
2120+ // and for client request bodies only when constructor unions require the
2121+ // corresponding validator helper during CLI payload validation.
2122+ if svr || containsUnionType (body .Type ) {
2123+ validateDef = codegen .ValidationCode (body , ut , httpctx , true , expr .IsAlias (ut ), false , "body" )
21222124 if validateDef != "" {
21232125 validateRef = fmt .Sprintf ("err = Validate%s(&body)" , varname )
21242126 }
@@ -2834,6 +2836,9 @@ func (sds *ServicesData) attributeTypeData(ut expr.UserType, req, ptr, server bo
28342836 // requests server-side and CLI.
28352837 // Alias types are validated inline in the parent type
28362838 validate = codegen .ValidationCode (ut .Attribute (), ut , hctx , true , expr .IsAlias (ut ), false , "body" )
2839+ if validate == "" && req && ! server && needsClientRequestBodyValidatorStub (ut ) {
2840+ validate = "// no validations"
2841+ }
28372842 }
28382843 if validate != "" {
28392844 validateRef = fmt .Sprintf ("err = Validate%s(v)" , name )
@@ -2850,6 +2855,44 @@ func (sds *ServicesData) attributeTypeData(ut expr.UserType, req, ptr, server bo
28502855 }
28512856}
28522857
2858+ func needsClientRequestBodyValidatorStub (ut expr.UserType ) bool {
2859+ if ut == nil || ut .Attribute () == nil || ut .Attribute ().Meta == nil {
2860+ return false
2861+ }
2862+ _ , ok := ut .Attribute ().Meta .Last ("oneof:type:tag" )
2863+ return ok
2864+ }
2865+
2866+ func containsUnionType (dt expr.DataType ) bool {
2867+ return containsUnionTypeRecursive (dt , make (map [string ]struct {}))
2868+ }
2869+
2870+ func containsUnionTypeRecursive (dt expr.DataType , seen map [string ]struct {}) bool {
2871+ switch actual := dt .(type ) {
2872+ case nil :
2873+ return false
2874+ case * expr.Union :
2875+ return true
2876+ case expr.UserType :
2877+ if _ , ok := seen [actual .ID ()]; ok {
2878+ return false
2879+ }
2880+ seen [actual .ID ()] = struct {}{}
2881+ return containsUnionTypeRecursive (actual .Attribute ().Type , seen )
2882+ case * expr.Object :
2883+ for _ , nat := range * actual {
2884+ if containsUnionTypeRecursive (nat .Attribute .Type , seen ) {
2885+ return true
2886+ }
2887+ }
2888+ case * expr.Array :
2889+ return containsUnionTypeRecursive (actual .ElemType .Type , seen )
2890+ case * expr.Map :
2891+ return containsUnionTypeRecursive (actual .KeyType .Type , seen ) || containsUnionTypeRecursive (actual .ElemType .Type , seen )
2892+ }
2893+ return false
2894+ }
2895+
28532896// httpContext returns a context for attributes of types used to marshal and
28542897// unmarshal HTTP requests and responses.
28552898//
0 commit comments