Repository: goadesign/goa
Pull request #2129 (2019-05-29 23:41:33)

Fixed ArrayOf(UInt32) conversion to use the right cast
The generated DecodeXyz function for uint32 arrays was using an int32() cast instead of uint32.


Patch:
diff --git a/README.md b/README.md
index 5d048e8e..9093687b 100644
--- a/README.md
+++ b/README.md
@@ -110,7 +110,7 @@ without having to make changes.
 Releases are tagged with the corresponding version number. There is also a
 branch for each major version (`v1`, `v2` and `v3`).
 
-Current Release: `v3.0.0`
+Current Release: `v3.0.1`
 
 ## Teaser
 
diff --git a/codegen/go_transform.go b/codegen/go_transform.go
index 708824a4..0e7ef6cd 100644
--- a/codegen/go_transform.go
+++ b/codegen/go_transform.go
@@ -399,7 +399,9 @@ func collectHelpers(source, target *expr.AttributeExpr, req bool, ta *TransformA
 			if err != nil {
 				return nil, err
 			}
-			data = append(data, d)
+			if d != nil {
+				data = append(data, d)
+			}
 		} else {
 			helpers, err := transformAttributeHelpers(st, tt, ta, seen)
 			if err != nil {
@@ -422,7 +424,9 @@ func collectHelpers(source, target *expr.AttributeExpr, req bool, ta *TransformA
 			if err != nil {
 				return nil, err
 			}
-			data = append(data, d)
+			if d != nil {
+				data = append(data, d)
+			}
 		} else {
 			helpers, err = transformAttributeHelpers(se, te, ta, seen)
 			if err != nil {
@@ -436,14 +440,16 @@ func collectHelpers(source, target *expr.AttributeExpr, req bool, ta *TransformA
 			if err != nil {
 				return nil, err
 			}
-			data = append(data, d)
+			if d != nil {
+				data = append(data, d)
+			}
 		}
 
 		// collect helpers
 		var err error
 		{
 			walkMatches(source, target, func(srcMatt, _ *expr.MappedAttributeExpr, srcc, tgtc *expr.AttributeExpr, n string) {
-				name := transformHelperName(source, target, ta)
+				name := transformHelperName(srcc, tgtc, ta)
 				if _, ok := seen[name]; ok {
 					return
 				}
diff --git a/codegen/service/endpoint.go b/codegen/service/endpoint.go
index f2e9be69..8bb6e7b9 100644
--- a/codegen/service/endpoint.go
+++ b/codegen/service/endpoint.go
@@ -207,6 +207,8 @@ func New{{ .VarName }}Endpoint(s {{ .ServiceVarName }}{{ range .Schemes }}, auth
 			{{- if eq .Type "Basic" }}
 				sc := security.BasicScheme{
 					Name: {{ printf "%q" .SchemeName }},
+					Scopes: []string{ {{- range .Scopes }}{{ printf "%q" . }}, {{ end }} },
+					RequiredScopes: []string{ {{- range $r.Scopes }}{{ printf "%q" . }}, {{ end }} },
 				}
 				{{- if .UsernamePointer }}
 				var user string
@@ -226,6 +228,8 @@ func New{{ .VarName }}Endpoint(s {{ .ServiceVarName }}{{ range .Schemes }}, auth
 			{{- else if eq .Type "APIKey" }}
 				sc := security.APIKeyScheme{
 					Name: {{ printf "%q" .SchemeName }},
+					Scopes: []string{ {{- range .Scopes }}{{ printf "%q" . }}, {{ end }} },
+					RequiredScopes: []string{ {{- range $r.Scopes }}{{ printf "%q" . }}, {{ end }} },
 				}
 				{{- if $s.CredPointer }}
 				var key string
diff --git a/codegen/service/service_data.go b/codegen/service/service_data.go
index 54ce71e1..c4409fac 100644
--- a/codegen/service/service_data.go
+++ b/codegen/service/service_data.go
@@ -854,6 +854,13 @@ func buildSchemeData(s *expr.SchemeExpr, m *expr.MethodExpr) *SchemeData {
 		user := codegen.Goify(userAtt, true)
 		passAtt := expr.TaggedAttribute(m.Payload, "security:password")
 		pass := codegen.Goify(passAtt, true)
+		var scopes []string
+		if len(s.Scopes) > 0 {
+			scopes = make([]string, len(s.Scopes))
+			for i, s := range s.Scopes {
+				scopes[i] = s.Name
+			}
+		}
 		return &SchemeData{
 			Type:             s.Kind.String(),
 			SchemeName:       s.SchemeName,
@@ -865,10 +872,18 @@ func buildSchemeData(s *expr.SchemeExpr, m *expr.MethodExpr) *SchemeData {
 			PasswordField:    pass,
 			PasswordPointer:  m.Payload.IsPrimitivePointer(passAtt, true),
 			PasswordRequired: m.Payload.IsRequired(passAtt),
+			Scopes:           scopes,
 		}
 	case expr.APIKeyKind:
 		if keyAtt := expr.TaggedAttribute(m.Payload, "security:apikey:"+s.SchemeName); keyAtt != "" {
 			key := codegen.Goify(keyAtt, true)
+			var scopes []string
+			if len(s.Scopes) > 0 {
+				scopes = make([]string, len(s.Scopes))
+				for i, s := range s.Scopes {
+					scopes[i] = s.Name
+				}
+			}
 			return &SchemeData{
 				Type:         s.Kind.String(),
 				Name:         s.Name,
@@ -877,6 +892,7 @@ func buildSchemeData(s *expr.SchemeExpr, m *expr.MethodExpr) *SchemeData {
 				CredPointer:  m.Payload.IsPrimitivePointer(keyAtt, true),
 				CredRequired: m.Payload.IsRequired(keyAtt),
 				KeyAttr:      keyAtt,
+				Scopes:       scopes,
 				In:           s.In,
 			}
 		}
diff --git a/dsl/security.go b/dsl/security.go
index f063bad5..dd33ba94 100644
--- a/dsl/security.go
+++ b/dsl/security.go
@@ -519,7 +519,7 @@ func TokenField(tag interface{}, name string, args ...interface{}) {
 // Scope has two uses: in JWTSecurity or OAuth2Security it defines a scope
 // supported by the scheme. In Security it lists required scopes.
 //
-// Scope must appear in Security, JWTSecurity or OAuth2Security.
+// Scope must appear in Security, BasicSecurity, APIKeySecurity, JWTSecurity or OAuth2Security.
 //
 // Scope accepts one or two arguments: the first argument is the scope name and
 // when used in JWTSecurity or OAuth2Security the second argument is a
diff --git a/expr/method.go b/expr/method.go
index 6b7c8e98..d4a16744 100644
--- a/expr/method.go
+++ b/expr/method.go
@@ -136,7 +136,7 @@ func (m *MethodExpr) Validate() error {
 			for _, scope := range r.Scopes {
 				found := false
 				for _, s := range r.Schemes {
-					if s.Kind == OAuth2Kind || s.Kind == JWTKind {
+					if s.Kind == BasicAuthKind || s.Kind == APIKeyKind || s.Kind == OAuth2Kind || s.Kind == JWTKind {
 						for _, se := range s.Scopes {
 							if se.Name == scope {
 								found = true
diff --git a/expr/security.go b/expr/security.go
index e518e60e..3a3029d5 100644
--- a/expr/security.go
+++ b/expr/security.go
@@ -65,7 +65,7 @@ type (
 		// Name refers to a header or parameter name, based on In's
 		// value.
 		Name string
-		// Scopes lists the JWT or OAuth2 scopes.
+		// Scopes lists the Basic, APIKey, JWT or OAuth2 scopes.
 		Scopes []*ScopeExpr
 		// Flows determine the oauth2 flows supported by this scheme.
 		Flows []*FlowExpr
diff --git a/http/codegen/openapi/openapi_v2_builder.go b/http/codegen/openapi/openapi_v2_builder.go
index 0d8e4965..b5356fad 100644
--- a/http/codegen/openapi/openapi_v2_builder.go
+++ b/http/codegen/openapi/openapi_v2_builder.go
@@ -143,6 +143,22 @@ func mustGenerate(meta expr.MetaExpr) bool {
 	return true
 }
 
+// addScopeDescription generates and adds required scopes to the scheme's description.
+func addScopeDescription(scopes []*expr.ScopeExpr, sd *SecurityDefinition) {
+	// Generate scopes to add to description
+	lines := []string{}
+	for _, scope := range scopes {
+		lines = append(lines, fmt.Sprintf("  * `%s`: %s", scope.Name, scope.Description))
+	}
+	// Add scope description only if scopes are defined
+	if len(lines) > 0 {
+		if sd.Description != "" {
+			sd.Description += "\n"
+		}
+		sd.Description += fmt.Sprintf("\n**Security Scopes**:\n%s", strings.Join(lines, "\n"))
+	}
+}
+
 // securitySpecFromExpr generates the OpenAPI security definitions from the
 // security design.
 func securitySpecFromExpr(root *expr.RootExpr) map[string]*SecurityDefinition {
@@ -159,21 +175,19 @@ func securitySpecFromExpr(root *expr.RootExpr) map[string]*SecurityDefinition {
 					switch s.Kind {
 					case expr.BasicAuthKind:
 						sd.Type = "basic"
+						addScopeDescription(s.Scopes, &sd)
 					case expr.APIKeyKind:
 						sd.Type = "apiKey"
 						sd.In = s.In
 						sd.Name = s.Name
+						addScopeDescription(s.Scopes, &sd)
 					case expr.JWTKind:
 						sd.Type = "apiKey"
 						// OpenAPI V2 spec does not support JWT scheme. Hence we add the scheme
 						// information to the description.
-						lines := []string{}
-						for _, scope := range s.Scopes {
-							lines = append(lines, fmt.Sprintf("  * `%s`: %s", scope.Name, scope.Description))
-						}
+						addScopeDescription(s.Scopes, &sd)
 						sd.In = s.In
 						sd.Name = s.Name
-						sd.Description += fmt.Sprintf("\n**Security Scopes**:\n%s", strings.Join(lines, "\n"))
 					case expr.OAuth2Kind:
 						sd.Type = "oauth2"
 						if scopesLen := len(s.Scopes); scopesLen > 0 {
@@ -648,15 +662,18 @@ func buildPathFromExpr(s *V2, root *expr.RootExpr, h *expr.HostExpr, route *expr
 				switch s.Kind {
 				case expr.OAuth2Kind:
 					requirement[s.Hash()] = append(requirement[s.Hash()], req.Scopes...)
-				case expr.JWTKind:
+				case expr.BasicAuthKind, expr.APIKeyKind, expr.JWTKind:
 					lines := make([]string, 0, len(req.Scopes))
 					for _, scope := range req.Scopes {
 						lines = append(lines, fmt.Sprintf("  * `%s`", scope))
 					}
-					if description != "" {
-						description += "\n"
+					// List scopes only if they are defined
+					if len(lines) > 0 {
+						if description != "" {
+							description += "\n"
+						}
+						description += fmt.Sprintf("\n**Required security scopes for %s**:\n%s", s.SchemeName, strings.Join(lines, "\n"))
 					}
-					description += fmt.Sprintf("\nRequired security scopes:\n%s", strings.Join(lines, "\n"))
 				}
 			}
 			requirements[i] = requirement
diff --git a/http/codegen/server.go b/http/codegen/server.go
index 24fc6ad4..f0441d4a 100644
--- a/http/codegen/server.go
+++ b/http/codegen/server.go
@@ -1023,7 +1023,7 @@ const typeConversionT = `{{- define "slice_conversion" }}
 			if err2 != nil {
 				err = goa.MergeErrors(err, goa.InvalidFieldTypeError({{ printf "%q" .VarName }}, {{ .VarName}}Raw, "array of unsigned integers"))
 			}
-			{{ .VarName }}[i] = int32(v)
+			{{ .VarName }}[i] = uint32(v)
 		{{- else if eq .Type.ElemType.Type.Name "uint64" }}
 			v, err2 := strconv.ParseUint(rv, 10, 64)
 			if err2 != nil {
diff --git a/pkg/version.go b/pkg/version.go
index 77d4693a..682bd3e3 100644
--- a/pkg/version.go
+++ b/pkg/version.go
@@ -12,7 +12,7 @@ const (
 	// Minor version number
 	Minor = 0
 	// Build number
-	Build = 0
+	Build = 1
 	// Suffix - set to empty string in release tag commits.
 	Suffix = ""
 )
diff --git a/security/scheme.go b/security/scheme.go
index eea35d93..222d555f 100644
--- a/security/scheme.go
+++ b/security/scheme.go
@@ -20,6 +20,11 @@ type (
 	BasicScheme struct {
 		// Name is the scheme name defined in the design.
 		Name string
+		// Scopes holds a list of scopes for the scheme.
+		Scopes []string
+		// RequiredScopes holds a list of scopes which are required
+		// by the scheme. It is a subset of Scopes field.
+		RequiredScopes []string
 	}
 
 	// APIKeyScheme represents the API key security scheme.
@@ -27,6 +32,11 @@ type (
 	APIKeyScheme struct {
 		// Name is the scheme name defined in the design.
 		Name string
+		// Scopes holds a list of scopes for the scheme.
+		Scopes []string
+		// RequiredScopes holds a list of scopes which are required
+		// by the scheme. It is a subset of Scopes field.
+		RequiredScopes []string
 	}
 
 	// JWTScheme represents an API key based scheme with support
@@ -83,6 +93,18 @@ type (
 	AuthJWTFunc func(ctx context.Context, token string, s *JWTScheme) (context.Context, error)
 )
 
+// Validate returns a non-nil error if scopes does not contain all of
+// Basic scheme's required scopes.
+func (s *BasicScheme) Validate(scopes []string) error {
+	return validateScopes(s.RequiredScopes, scopes)
+}
+
+// Validate returns a non-nil error if scopes does not contain all of
+// APIKey scheme's required scopes.
+func (s *APIKeyScheme) Validate(scopes []string) error {
+	return validateScopes(s.RequiredScopes, scopes)
+}
+
 // Validate returns a non-nil error if scopes does not contain all of
 // OAuth2 scheme's required scopes.
 func (s *OAuth2Scheme) Validate(scopes []string) error {


Test patch:
diff --git a/codegen/go_transform_helpers_test.go b/codegen/go_transform_helpers_test.go
new file mode 100644
index 00000000..86b06a0e
--- /dev/null
+++ b/codegen/go_transform_helpers_test.go
@@ -0,0 +1,58 @@
+package codegen
+
+import (
+	"testing"
+
+	"goa.design/goa/v3/codegen/testdata"
+	"goa.design/goa/v3/expr"
+)
+
+func TestGoTransformHelpers(t *testing.T) {
+	root := RunDSL(t, testdata.TestTypesDSL)
+	var (
+		scope = NewNameScope()
+		// types to test
+		simple    = root.UserType("Simple")
+		recursive = root.UserType("Recursive")
+		composite = root.UserType("Composite")
+		deep      = root.UserType("Deep")
+		// attribute contexts used in test cases
+		defaultCtx = NewAttributeContext(false, false, true, "", scope)
+	)
+	tc := []struct {
+		Name        string
+		Type        expr.DataType
+		HelperNames []string
+	}{
+		{"simple", simple, []string{}},
+		{"recursive", recursive, []string{"transformRecursiveToRecursive"}},
+		{"composite", composite, []string{"transformSimpleToSimple"}},
+		{"deep", deep, []string{"transformCompositeToComposite", "transformSimpleToSimple"}},
+	}
+	for _, c := range tc {
+		t.Run(c.Name, func(t *testing.T) {
+			if c.Type == nil {
+				t.Fatal("source type not found in testdata")
+			}
+			_, funcs, err := GoTransform(&expr.AttributeExpr{Type: c.Type}, &expr.AttributeExpr{Type: c.Type}, "source", "target", defaultCtx, defaultCtx, "")
+			if err != nil {
+				t.Fatal(err)
+			}
+			if len(funcs) != len(c.HelperNames) {
+				t.Errorf("invalid helpers count, got: %d, expected %d", len(funcs), len(c.HelperNames))
+			} else {
+				var diffs []string
+				actual := make([]string, len(funcs))
+				for i, f := range funcs {
+					actual[i] = f.Name
+					if c.HelperNames[i] != f.Name {
+						diffs = append(diffs, f.Name)
+					}
+				}
+				if len(diffs) > 0 {
+					t.Errorf("invalid helper names, got: %v, expected: %v", actual, c.HelperNames)
+				}
+			}
+		})
+	}
+}
diff --git a/codegen/go_transform_test.go b/codegen/go_transform_test.go
index 39de99af..f5aaed92 100644
--- a/codegen/go_transform_test.go
+++ b/codegen/go_transform_test.go
@@ -90,8 +90,8 @@ func TestGoTransform(t *testing.T) {
 
 			// others
 			{"recursive-to-recursive", recursive, recursive, defaultCtx, defaultCtx, srcTgtUseDefaultRecursiveToRecursiveCode},
-			{"recursiveArray-to-recursiveArray", recursiveArray, recursiveArray, defaultCtx, defaultCtx, srcTgtUseDefaultRecursiveArrayToRecursiveArrayCode},
-			{"recursiveMap-to-recursiveMap", recursiveMap, recursiveMap, defaultCtx, defaultCtx, srcTgtUseDefaultRecursiveMapToRecursiveMapCode},
+			{"recursive-array-to-recursive-array", recursiveArray, recursiveArray, defaultCtx, defaultCtx, srcTgtUseDefaultRecursiveArrayToRecursiveArrayCode},
+			{"recursive-map-to-recursive-map", recursiveMap, recursiveMap, defaultCtx, defaultCtx, srcTgtUseDefaultRecursiveMapToRecursiveMapCode},
 			{"composite-to-custom-field", composite, customField, defaultCtx, defaultCtx, srcTgtUseDefaultCompositeToCustomFieldCode},
 			{"custom-field-to-composite", customField, composite, defaultCtx, defaultCtx, srcTgtUseDefaultCustomFieldToCompositeCode},
 			{"composite-to-custom-field-pkg", composite, customField, defaultCtx, defaultCtxPkg, srcTgtUseDefaultCompositeToCustomFieldPkgCode},
diff --git a/codegen/service/security_test.go b/codegen/service/security_test.go
index cc576f81..1d544903 100644
--- a/codegen/service/security_test.go
+++ b/codegen/service/security_test.go
@@ -48,6 +48,7 @@ func TestSecureEndpoint(t *testing.T) {
 		Code string
 	}{
 		{"with-required-scopes", testdata.EndpointWithRequiredScopesDSL, testdata.EndpointWithRequiredScopesCode},
+		{"with-optional-required-scopes", testdata.EndpointWithOptionalRequiredScopesDSL, testdata.EndpointWithOptionalRequiredScopesCode},
 		{"with-api-key-override", testdata.EndpointWithAPIKeyOverrideDSL, testdata.EndpointWithAPIKeyOverrideCode},
 		{"with-oauth2", testdata.EndpointWithOAuth2DSL, testdata.EndpointWithOAuth2Code},
 	}
diff --git a/codegen/service/testdata/security_dsls.go b/codegen/service/testdata/security_dsls.go
index 984512ea..2127b6b3 100644
--- a/codegen/service/testdata/security_dsls.go
+++ b/codegen/service/testdata/security_dsls.go
@@ -4,7 +4,11 @@ import (
 	. "goa.design/goa/v3/dsl"
 )
 
-var BasicAuth = BasicAuthSecurity("basic")
+var BasicAuth = BasicAuthSecurity("basic", func() {
+	Scope("api:read", "Read-only access")
+	Scope("api:write", "Read and write access")
+	Scope("api:admin", "Admin access")
+})
 
 var JWTAuth = JWTSecurity("jwt", func() {
 	Scope("api:read", "Read-only access")
@@ -12,7 +16,11 @@ var JWTAuth = JWTSecurity("jwt", func() {
 	Scope("api:admin", "Admin access")
 })
 
-var APIKeyAuth = APIKeySecurity("api_key")
+var APIKeyAuth = APIKeySecurity("api_key", func() {
+	Scope("api:read", "Read-only access")
+	Scope("api:write", "Read and write access")
+	Scope("api:admin", "Admin access")
+})
 
 var OAuth2AuthorizationCode = OAuth2Security("authCode", func() {
 	AuthorizationCodeFlow("/authorization", "/token", "/refresh")
@@ -109,6 +117,24 @@ var EndpointWithRequiredScopesDSL = func() {
 	})
 }
 
+var EndpointWithOptionalRequiredScopesDSL = func() {
+	Service("EndpointWithOptionalRequiredScopes", func() {
+		Method("SecureWithOptionalRequiredScopes", func() {
+			Security(BasicAuth, func() {
+				Scope("api:read")
+				Scope("api:write")
+			})
+			Payload(func() {
+				Username("user", String)
+				Password("pass", String)
+			})
+			HTTP(func() {
+				GET("/")
+			})
+		})
+	})
+}
+
 var EndpointWithAPIKeyOverrideDSL = func() {
 	Service("EndpointWithAPIKeyOverride", func() {
 		Security(BasicAuth)
diff --git a/codegen/service/testdata/security_functions.go b/codegen/service/testdata/security_functions.go
index 4844eb40..03e57db3 100644
--- a/codegen/service/testdata/security_functions.go
+++ b/codegen/service/testdata/security_functions.go
@@ -67,6 +67,35 @@ func NewSecureWithRequiredScopesEndpoint(s Service, authJWTFn security.AuthJWTFu
 }
 `
 
+var EndpointWithOptionalRequiredScopesCode = `// NewSecureWithOptionalRequiredScopesEndpoint returns an endpoint function
+// that calls the method "SecureWithOptionalRequiredScopes" of service
+// "EndpointWithOptionalRequiredScopes".
+func NewSecureWithOptionalRequiredScopesEndpoint(s Service, authBasicFn security.AuthBasicFunc) goa.Endpoint {
+	return func(ctx context.Context, req interface{}) (interface{}, error) {
+		p := req.(*SecureWithOptionalRequiredScopesPayload)
+		var err error
+		sc := security.BasicScheme{
+			Name:           "basic",
+			Scopes:         []string{"api:read", "api:write", "api:admin"},
+			RequiredScopes: []string{"api:read", "api:write"},
+		}
+		var user string
+		if p.User != nil {
+			user = *p.User
+		}
+		var pass string
+		if p.Pass != nil {
+			pass = *p.Pass
+		}
+		ctx, err = authBasicFn(ctx, user, pass, &sc)
+		if err != nil {
+			return nil, err
+		}
+		return nil, s.SecureWithOptionalRequiredScopes(ctx, p)
+	}
+}
+`
+
 var EndpointWithAPIKeyOverrideCode = `// NewSecureWithAPIKeyOverrideEndpoint returns an endpoint function that calls
 // the method "SecureWithAPIKeyOverride" of service
 // "EndpointWithAPIKeyOverride".
@@ -75,7 +104,9 @@ func NewSecureWithAPIKeyOverrideEndpoint(s Service, authAPIKeyFn security.AuthAP
 		p := req.(*SecureWithAPIKeyOverridePayload)
 		var err error
 		sc := security.APIKeyScheme{
-			Name: "api_key",
+			Name:           "api_key",
+			Scopes:         []string{"api:read", "api:write", "api:admin"},
+			RequiredScopes: []string{},
 		}
 		var key string
 		if p.Key != nil {
diff --git a/codegen/testdata/types_dsl.go b/codegen/testdata/types_dsl.go
index 49923a96..d04d1255 100644
--- a/codegen/testdata/types_dsl.go
+++ b/codegen/testdata/types_dsl.go
@@ -90,7 +90,7 @@ var TestTypesDSL = func() {
 			Attribute("map_array", ArrayOf(MapOf(Int, String)))
 		})
 
-		_ = Type("Composite", func() {
+		Composite = Type("Composite", func() {
 			Attribute("required_string", String)
 			Attribute("default_int", Int)
 			Attribute("type", Simple)
@@ -98,6 +98,11 @@ var TestTypesDSL = func() {
 			Attribute("array", ArrayOf(String))
 		})
 
+		_ = Type("Deep", func() {
+			Attribute("string", String)
+			Attribute("inner", Composite)
+		})
+
 		_ = Type("CompositeWithCustomField", func() {
 			Attribute("required_string", String, func() {
 				Meta("struct:field:name", "my_string")
diff --git a/http/codegen/client_body_types_test.go b/http/codegen/client_body_types_test.go
index ea79f027..3a14c06e 100644
--- a/http/codegen/client_body_types_test.go
+++ b/http/codegen/client_body_types_test.go
@@ -191,6 +191,7 @@ type MethodARequestBody struct {
 	Array  []float32            ` + "`" + `form:"array" json:"array" xml:"array"` + "`" + `
 	Map    map[uint]interface{} ` + "`" + `form:"map,omitempty" json:"map,omitempty" xml:"map,omitempty"` + "`" + `
 	Object *BPayloadRequestBody ` + "`" + `form:"object" json:"object" xml:"object"` + "`" + `
+	DupObj *BPayloadRequestBody ` + "`" + `form:"dup_obj,omitempty" json:"dup_obj,omitempty" xml:"dup_obj,omitempty"` + "`" + `
 }
 
 // BPayloadRequestBody is used to define fields on request body types.
@@ -222,6 +223,9 @@ func NewMethodARequestBody(p *servicemixedpayloadinbody.APayload) *MethodAReques
 	if p.Object != nil {
 		body.Object = marshalServicemixedpayloadinbodyBPayloadToBPayloadRequestBody(p.Object)
 	}
+	if p.DupObj != nil {
+		body.DupObj = marshalServicemixedpayloadinbodyBPayloadToBPayloadRequestBody(p.DupObj)
+	}
 	return body
 }
 `
diff --git a/http/codegen/server_types_test.go b/http/codegen/server_types_test.go
index 8692a29e..cb7b2e4e 100644
--- a/http/codegen/server_types_test.go
+++ b/http/codegen/server_types_test.go
@@ -45,6 +45,7 @@ type MethodARequestBody struct {
 	Array  []float32            ` + "`" + `form:"array,omitempty" json:"array,omitempty" xml:"array,omitempty"` + "`" + `
 	Map    map[uint]interface{} ` + "`" + `form:"map,omitempty" json:"map,omitempty" xml:"map,omitempty"` + "`" + `
 	Object *BPayloadRequestBody ` + "`" + `form:"object,omitempty" json:"object,omitempty" xml:"object,omitempty"` + "`" + `
+	DupObj *BPayloadRequestBody ` + "`" + `form:"dup_obj,omitempty" json:"dup_obj,omitempty" xml:"dup_obj,omitempty"` + "`" + `
 }
 
 // BPayloadRequestBody is used to define fields on request body types.
@@ -72,6 +73,9 @@ func NewMethodAAPayload(body *MethodARequestBody) *servicemixedpayloadinbody.APa
 		}
 	}
 	v.Object = unmarshalBPayloadRequestBodyToServicemixedpayloadinbodyBPayload(body.Object)
+	if body.DupObj != nil {
+		v.DupObj = unmarshalBPayloadRequestBodyToServicemixedpayloadinbodyBPayload(body.DupObj)
+	}
 	return v
 }
 
@@ -88,6 +92,11 @@ func ValidateMethodARequestBody(body *MethodARequestBody) (err error) {
 			err = goa.MergeErrors(err, err2)
 		}
 	}
+	if body.DupObj != nil {
+		if err2 := ValidateBPayloadRequestBody(body.DupObj); err2 != nil {
+			err = goa.MergeErrors(err, err2)
+		}
+	}
 	return
 }
 
diff --git a/http/codegen/testdata/openapi_v2/TestSections/security_file0.golden b/http/codegen/testdata/openapi_v2/TestSections/security_file0.golden
index 3c99b629..e4b2902b 100644
--- a/http/codegen/testdata/openapi_v2/TestSections/security_file0.golden
+++ b/http/codegen/testdata/openapi_v2/TestSections/security_file0.golden
@@ -1,1 +1,1 @@
-{"swagger":"2.0","info":{"title":"","version":""},"host":"localhost:80","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpointA testService","description":"\nRequired security scopes:\n  * `api:read`","operationId":"testService#testEndpointA","parameters":[{"name":"k","in":"query","required":true,"type":"string"},{"name":"Token","in":"header","required":true,"type":"string"},{"name":"X-Authorization","in":"header","required":true,"type":"string"},{"name":"Authorization","in":"header","description":"Basic Auth security using Basic scheme (https://tools.ietf.org/html/rfc7617)","required":true,"type":"string"}],"responses":{"200":{"description":"OK response."}},"schemes":["http"],"security":[{"api_key_query_k":[],"basic_header_Authorization":[],"jwt_header_X-Authorization":[],"oauth2_header_Token":["api:read"]}]},"post":{"tags":["testService"],"summary":"testEndpointB testService","operationId":"testService#testEndpointB","parameters":[{"name":"auth","in":"query","required":true,"type":"string"},{"name":"Authorization","in":"header","required":true,"type":"string"}],"responses":{"200":{"description":"OK response."}},"schemes":["http"],"security":[{"api_key_header_Authorization":[]},{"oauth2_query_auth":["api:read","api:write"]}]}}},"securityDefinitions":{"api_key_header_Authorization":{"type":"apiKey","description":"Secures endpoint by requiring an API key.","name":"Authorization","in":"header"},"api_key_query_k":{"type":"apiKey","description":"Secures endpoint by requiring an API key.","name":"k","in":"query"},"basic_header_Authorization":{"type":"basic","description":"Basic authentication used to authenticate security principal during signin"},"jwt_header_X-Authorization":{"type":"apiKey","description":"Secures endpoint by requiring a valid JWT token retrieved via the signin endpoint. Supports scopes \"api:read\" and \"api:write\".\n**Security Scopes**:\n  * `api:read`: Read-only access\n  * `api:write`: Read and write access","name":"X-Authorization","in":"header"},"oauth2_header_Token":{"type":"oauth2","description":"Secures endpoint by requiring a valid OAuth2 token retrieved via the signin endpoint. Supports scopes \"api:read\" and \"api:write\".","flow":"accessCode","authorizationUrl":"http://goa.design/authorization","tokenUrl":"http://goa.design/token","scopes":{"api:read":"Read-only access","api:write":"Read and write access"}},"oauth2_query_auth":{"type":"oauth2","description":"Secures endpoint by requiring a valid OAuth2 token retrieved via the signin endpoint. Supports scopes \"api:read\" and \"api:write\".","flow":"accessCode","authorizationUrl":"http://goa.design/authorization","tokenUrl":"http://goa.design/token","scopes":{"api:read":"Read-only access","api:write":"Read and write access"}}}}
\ No newline at end of file
+{"swagger":"2.0","info":{"title":"","version":""},"host":"localhost:80","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpointA testService","description":"\n**Required security scopes for basic**:\n  * `api:read`\n\n**Required security scopes for jwt**:\n  * `api:read`\n\n**Required security scopes for api_key**:\n  * `api:read`","operationId":"testService#testEndpointA","parameters":[{"name":"k","in":"query","required":true,"type":"string"},{"name":"Token","in":"header","required":true,"type":"string"},{"name":"X-Authorization","in":"header","required":true,"type":"string"},{"name":"Authorization","in":"header","description":"Basic Auth security using Basic scheme (https://tools.ietf.org/html/rfc7617)","required":true,"type":"string"}],"responses":{"200":{"description":"OK response."}},"schemes":["http"],"security":[{"api_key_query_k":[],"basic_header_Authorization":[],"jwt_header_X-Authorization":[],"oauth2_header_Token":["api:read"]}]},"post":{"tags":["testService"],"summary":"testEndpointB testService","operationId":"testService#testEndpointB","parameters":[{"name":"auth","in":"query","required":true,"type":"string"},{"name":"Authorization","in":"header","required":true,"type":"string"}],"responses":{"200":{"description":"OK response."}},"schemes":["http"],"security":[{"api_key_header_Authorization":[]},{"oauth2_query_auth":["api:read","api:write"]}]}}},"securityDefinitions":{"api_key_header_Authorization":{"type":"apiKey","description":"Secures endpoint by requiring an API key.","name":"Authorization","in":"header"},"api_key_query_k":{"type":"apiKey","description":"Secures endpoint by requiring an API key.","name":"k","in":"query"},"basic_header_Authorization":{"type":"basic","description":"Basic authentication used to authenticate security principal during signin"},"jwt_header_X-Authorization":{"type":"apiKey","description":"Secures endpoint by requiring a valid JWT token retrieved via the signin endpoint. Supports scopes \"api:read\" and \"api:write\".\n\n**Security Scopes**:\n  * `api:read`: Read-only access\n  * `api:write`: Read and write access","name":"X-Authorization","in":"header"},"oauth2_header_Token":{"type":"oauth2","description":"Secures endpoint by requiring a valid OAuth2 token retrieved via the signin endpoint. Supports scopes \"api:read\" and \"api:write\".","flow":"accessCode","authorizationUrl":"http://goa.design/authorization","tokenUrl":"http://goa.design/token","scopes":{"api:read":"Read-only access","api:write":"Read and write access"}},"oauth2_query_auth":{"type":"oauth2","description":"Secures endpoint by requiring a valid OAuth2 token retrieved via the signin endpoint. Supports scopes \"api:read\" and \"api:write\".","flow":"accessCode","authorizationUrl":"http://goa.design/authorization","tokenUrl":"http://goa.design/token","scopes":{"api:read":"Read-only access","api:write":"Read and write access"}}}}
\ No newline at end of file
diff --git a/http/codegen/testdata/openapi_v2/TestSections/security_file1.golden b/http/codegen/testdata/openapi_v2/TestSections/security_file1.golden
index 13be03bb..39ff21a7 100644
--- a/http/codegen/testdata/openapi_v2/TestSections/security_file1.golden
+++ b/http/codegen/testdata/openapi_v2/TestSections/security_file1.golden
@@ -19,7 +19,13 @@ paths:
       summary: testEndpointA testService
       description: |2-
 
-        Required security scopes:
+        **Required security scopes for basic**:
+          * `api:read`
+
+        **Required security scopes for jwt**:
+          * `api:read`
+
+        **Required security scopes for api_key**:
           * `api:read`
       operationId: testService#testEndpointA
       parameters:
@@ -94,6 +100,7 @@ securityDefinitions:
     type: apiKey
     description: |-
       Secures endpoint by requiring a valid JWT token retrieved via the signin endpoint. Supports scopes "api:read" and "api:write".
+
       **Security Scopes**:
         * `api:read`: Read-only access
         * `api:write`: Read and write access
diff --git a/http/codegen/testdata/payload_decode_functions.go b/http/codegen/testdata/payload_decode_functions.go
index 8dfa8515..01b686f1 100644
--- a/http/codegen/testdata/payload_decode_functions.go
+++ b/http/codegen/testdata/payload_decode_functions.go
@@ -1083,7 +1083,7 @@ func DecodeMethodQueryArrayUInt32Request(mux goahttp.Muxer, decoder func(*http.R
 					if err2 != nil {
 						err = goa.MergeErrors(err, goa.InvalidFieldTypeError("q", qRaw, "array of unsigned integers"))
 					}
-					q[i] = int32(v)
+					q[i] = uint32(v)
 				}
 			}
 		}
@@ -1117,7 +1117,7 @@ func DecodeMethodQueryArrayUInt32ValidateRequest(mux goahttp.Muxer, decoder func
 				if err2 != nil {
 					err = goa.MergeErrors(err, goa.InvalidFieldTypeError("q", qRaw, "array of unsigned integers"))
 				}
-				q[i] = int32(v)
+				q[i] = uint32(v)
 			}
 		}
 		if len(q) < 1 {
diff --git a/http/codegen/testdata/payload_dsls.go b/http/codegen/testdata/payload_dsls.go
index 4589b13c..9c76b544 100644
--- a/http/codegen/testdata/payload_dsls.go
+++ b/http/codegen/testdata/payload_dsls.go
@@ -2590,6 +2590,7 @@ var MixedPayloadInBodyDSL = func() {
 		Attribute("array", ArrayOf(Float32))
 		Attribute("map", MapOf(UInt, Any))
 		Attribute("object", BPayload)
+		Attribute("dup_obj", BPayload)
 		Required("array", "object")
 	})
 	Service("ServiceMixedPayloadInBody", func() {