Repository: goadesign/goa (go)

Issue:
Query parameter mapped via MapParams is not present in OpenAPI spec
Using Goa version v3.7.2

[Design example to reproduce](https://github.com/nya1/goa-mapparams-repro/tree/master/design/design.go)

The expected `metadata` Attribute of `MapOf(String, String)` type is
mapped to the HTTP transport as  `MapParams("metadata")`

The `metadata` query parameter can be passed to the URL as the following:
`GET /multiply/?metadata[testKey]=val`

The service will correctly receive `map[testKey:val]`, but the [OpenAPI generated](https://github.com/nya1/goa-mapparams-repro/tree/master/gen/http/openapi3.yaml#L15)
it's not listing the `metadata` in the endpoint.

## Expected

`metadata` query parameter to be present in OpenAPI like the other mapped attributes, example:

```
      parameters:
      - in: query
        name: metadata
        description: this should be on openapi
        required: false
        schema:
          type: object
          additionalProperties: true
          example:
            testKey: val
        style: deepObject
```


You can find all files / setup to reproduce the issue: https://github.com/nya1/goa-mapparams-repro

Pull request description:
Handle MapParams in OpenAPI specs
Fix #3053

Patch:
diff --git a/http/codegen/openapi/v3/builder.go b/http/codegen/openapi/v3/builder.go
index 5fbf287a..5859d55c 100644
--- a/http/codegen/openapi/v3/builder.go
+++ b/http/codegen/openapi/v3/builder.go
@@ -261,6 +261,23 @@ func buildOperation(key string, r *expr.RouteExpr, bodies *EndpointBodies, rand
 	{
 		ps := paramsFromPath(e.Params, key, rand)
 		ps = append(ps, paramsFromHeadersAndCookies(e, rand)...)
+		if e.MapQueryParams != nil {
+			name := *e.MapQueryParams
+			if name == "" {
+				name = "payload"
+			}
+			ps = append(ps, &Parameter{
+				Name:        name,
+				Description: "Query parameters",
+				In:          "query",
+				Required:    name == "payload" || e.MethodExpr.Payload.IsRequired(name),
+				Schema: &openapi.Schema{
+					Type:                 "object",
+					AdditionalProperties: true,
+				},
+				Style: "deepObject",
+			})
+		}
 		params = make([]*ParameterRef, len(ps))
 		for i, p := range ps {
 			params[i] = &ParameterRef{Value: p}


Test patch:
diff --git a/http/codegen/openapi/v3/builder_test.go b/http/codegen/openapi/v3/builder_test.go
index f74e33d4..f1b04efa 100644
--- a/http/codegen/openapi/v3/builder_test.go
+++ b/http/codegen/openapi/v3/builder_test.go
@@ -149,6 +149,12 @@ func TestBuildOperation(t *testing.T) {
 
 		ExpectedRequestBody: &requestBody{"", tobj("name", tstring), true},
 		ExpectedResponses:   responses{"204": {Description: "No Content response."}},
+	}, {
+		Name: "request_map_params",
+		DSL:  dsls.RequestMapParams(svcName, "request_map_params"),
+
+		ExpectedParameters: []param{{Name: "param", In: "query", Description: "Query parameters", Style: "deepObject", Type: tobj()}},
+		ExpectedResponses:  responses{"204": {Description: "No Content response."}},
 	}, {
 		Name: "response_array_of_string",
 		DSL:  dsls.ResponseArrayOfString(svcName, "response_array_of_string"),
diff --git a/http/codegen/openapi/v3/testdata/dsls/operations.go b/http/codegen/openapi/v3/testdata/dsls/operations.go
index 612407a2..dfeae3d4 100644
--- a/http/codegen/openapi/v3/testdata/dsls/operations.go
+++ b/http/codegen/openapi/v3/testdata/dsls/operations.go
@@ -45,6 +45,22 @@ var RequestObjectBody = func(svc, met string) func() {
 	}
 }
 
+var RequestMapParams = func(svc, met string) func() {
+	return func() {
+		var _ = Service(svc, func() {
+			Method(met, func() {
+				Payload(func() {
+					Attribute("param", MapOf(String, String))
+				})
+				HTTP(func() {
+					POST("/")
+					MapParams("param")
+				})
+			})
+		})
+	}
+}
+
 var RequestStreamingStringBody = func(svc, met string) func() {
 	return func() {
 		var _ = Service(svc, func() {