Repository: goadesign/goa
Pull request #3446 (2024-01-12 04:13:10)

Corrected MapParams Behavior to Avoid Unintended Filtering
This PR addresses a bug in the `MapParams` DSL function. The issue stemmed from an unintended behavior where `MapParams` was filtering query string parameters based on their prefixes.

### Clarification of the Issue:

In the existing implementation, consider this design:

```go
var _ = Service("exampleService", func() {
    Method("exampleMethod", func() {
        Payload(func() {
            Attribute("id", String)
            Attribute("query", MapOf(String, String))
        })
        HTTP(func() {
            GET("/resource/{id}")
            MapParams("query")
        })
    })
})
```

The intention here is for `MapParams("query")` to map all query string parameters to the `query` attribute in the payload. However, due to the bug, the function was incorrectly filtering these parameters. It was only including parameters named like `query[foo]` or `query[bar]`, and excluding others.

### The PR Fix:

This update corrects `MapParams` to function as intended: it now maps all query string parameters, irrespective of their naming or prefixes, to the designated map attribute in the payload. This behavior distinguishes `MapParams` from `Param`, which is specifically used for filtering and including named query string parameters.


Patch:
diff --git a/README.md b/README.md
index ba3fcde5..e53e5e10 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,8 @@
   </p>
 </p>
 
-<table style="border-collapse: collapse; width: 100%;">
+<div align="center">
+<table style="border-collapse: collapse; width: 500px;">
     <tr>
         <td style="border: none; width: 200px;">
             <a href="https://chat.openai.com/g/g-mLuQDGyro-goa-design-wizard">
@@ -24,17 +25,24 @@
             </a>
         </td>
         <td style="border: none;">
+            <div style="text-align:center">
             <h1>Goa Design Wizard</h1>
+            </div>
+            <p>
             Use the <a href="https://chat.openai.com/g/g-mLuQDGyro-goa-design-wizard">Goa Design Wizard</a> to:
+            </p>
             <ul>
-                <li>Create Goa designs for new services in seconds</li>
+                <li>Create Goa designs in seconds</li>
                 <li>Review existing designs</li>
                 <li>Explore the Goa DSL</li>
             </ul>
-            <p>(requires a <a href="https://openai.com/blog/chatgpt-plus">ChatGPT Plus</a> subscription)</p>
+            <p>
+            (requires a <a href="https://openai.com/blog/chatgpt-plus">ChatGPT Plus</a> subscription)<br/><br/>
+            </p>
         </td>
     </tr>
 </table>
+</div>
 
 # Overview
 
diff --git a/go.mod b/go.mod
index 498fba8d..057ff313 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
 	github.com/sergi/go-diff v1.3.1
 	github.com/stretchr/testify v1.8.4
 	golang.org/x/text v0.14.0
-	golang.org/x/tools v0.16.1
+	golang.org/x/tools v0.17.0
 	google.golang.org/grpc v1.60.1
 	google.golang.org/protobuf v1.32.0
 	gopkg.in/yaml.v3 v3.0.1
@@ -33,7 +33,7 @@ require (
 	github.com/perimeterx/marshmallow v1.1.5 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	golang.org/x/mod v0.14.0 // indirect
-	golang.org/x/net v0.19.0 // indirect
+	golang.org/x/net v0.20.0 // indirect
 	golang.org/x/sys v0.16.0 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect
 )
diff --git a/go.sum b/go.sum
index dd708458..c5a175c7 100644
--- a/go.sum
+++ b/go.sum
@@ -64,18 +64,18 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
 github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
 golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
 golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
-golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
 golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
 golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
-golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
+golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
+golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
 google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
 google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
diff --git a/http/codegen/server.go b/http/codegen/server.go
index f24d8317..dafdd448 100644
--- a/http/codegen/server.go
+++ b/http/codegen/server.go
@@ -779,9 +779,7 @@ const requestElementsT = `{{- define "request_elements" }}
 		if len({{ .VarName }}Raw) != 0 {
 		{{- end }}
 		for keyRaw, valRaw := range {{ .VarName }}Raw {
-			if strings.HasPrefix(keyRaw, "{{ .HTTPName }}[") {
-				{{- template "map_conversion" (mapQueryDecodeData .Type .VarName 0) }}
-			}
+			{{- template "map_conversion" (mapQueryDecodeData .Type .VarName 0) }}
 		}
 		{{- if or .DefaultValue (not .Required) }}
 		}


Test patch:
diff --git a/http/codegen/testdata/payload_decode_functions.go b/http/codegen/testdata/payload_decode_functions.go
index 19e3be6c..a6dccb67 100644
--- a/http/codegen/testdata/payload_decode_functions.go
+++ b/http/codegen/testdata/payload_decode_functions.go
@@ -4906,23 +4906,21 @@ func DecodeMethodMapQueryObjectRequest(mux goahttp.Muxer, decoder func(*http.Req
 				err = goa.MergeErrors(err, goa.MissingFieldError("c", "query string"))
 			}
 			for keyRaw, valRaw := range cRaw {
-				if strings.HasPrefix(keyRaw, "c[") {
-					if c == nil {
-						c = make(map[int][]string)
-					}
-					var keya int
-					{
-						openIdx := strings.IndexRune(keyRaw, '[')
-						closeIdx := strings.IndexRune(keyRaw, ']')
-						keyaRaw := keyRaw[openIdx+1 : closeIdx]
-						v, err2 := strconv.ParseInt(keyaRaw, 10, strconv.IntSize)
-						if err2 != nil {
-							err = goa.MergeErrors(err, goa.InvalidFieldTypeError("query", keyaRaw, "integer"))
-						}
-						keya = int(v)
+				if c == nil {
+					c = make(map[int][]string)
+				}
+				var keya int
+				{
+					openIdx := strings.IndexRune(keyRaw, '[')
+					closeIdx := strings.IndexRune(keyRaw, ']')
+					keyaRaw := keyRaw[openIdx+1 : closeIdx]
+					v, err2 := strconv.ParseInt(keyaRaw, 10, strconv.IntSize)
+					if err2 != nil {
+						err = goa.MergeErrors(err, goa.InvalidFieldTypeError("query", keyaRaw, "integer"))
 					}
-					c[keya] = valRaw
+					keya = int(v)
 				}
+				c[keya] = valRaw
 			}
 		}
 		if err != nil {