Repository: goadesign/goa
Pull request #2323 (2019-10-17 04:05:30)

Fix incorrect fields of operations in generated OpenAPI definitions
This is backport of #2313 to v3.

raphael: Thank you!

Patch:
diff --git a/http/codegen/openapi/openapi_v2_builder.go b/http/codegen/openapi/openapi_v2_builder.go
index 89d42152..e2a13b08 100644
--- a/http/codegen/openapi/openapi_v2_builder.go
+++ b/http/codegen/openapi/openapi_v2_builder.go
@@ -640,10 +640,19 @@ func buildPathFromExpr(s *V2, root *expr.RootExpr, h *expr.HostExpr, route *expr
 			responses[strconv.Itoa(er.Response.StatusCode)] = resp
 		}
 
+		var consumes []string
+		if endpoint.MultipartRequest {
+			consumes = []string{"multipart/form-data"}
+		}
+
 		if endpoint.Body.Type != expr.Empty {
+			in := "body"
+			if endpoint.MultipartRequest {
+				in = "formData"
+			}
 			pp := &Parameter{
 				Name:        endpoint.Body.Type.Name(),
-				In:          "body",
+				In:          in,
 				Description: endpoint.Body.Description,
 				Required:    true,
 				Schema:      AttributeTypeSchemaWithPrefix(root.API, endpoint.Body, codegen.Goify(endpoint.Service.Name(), true)),
@@ -720,6 +729,7 @@ func buildPathFromExpr(s *V2, root *expr.RootExpr, h *expr.HostExpr, route *expr
 			ExternalDocs: docsFromExpr(endpoint.MethodExpr.Docs),
 			OperationID:  operationID,
 			Parameters:   params,
+			Consumes:     consumes,
 			Produces:     produces,
 			Responses:    responses,
 			Schemes:      schemes,


Test patch:
diff --git a/http/codegen/openapi/openapi_v2_builder_test.go b/http/codegen/openapi/openapi_v2_builder_test.go
index ed354798..ceaba5f2 100644
--- a/http/codegen/openapi/openapi_v2_builder_test.go
+++ b/http/codegen/openapi/openapi_v2_builder_test.go
@@ -49,3 +49,91 @@ func TestBuildPathFromFileServer(t *testing.T) {
 		})
 	}
 }
+
+func TestBuildPathFromExpr(t *testing.T) {
+	cases := map[string]struct {
+		multipartRequest bool
+		expected         Operation
+	}{
+		"multipart request": {
+			multipartRequest: true,
+			expected: Operation{
+				Consumes: []string{"multipart/form-data"},
+				Parameters: []*Parameter{
+					&Parameter{
+						In: "formData",
+					},
+				},
+			},
+		},
+		"non multipart request": {
+			multipartRequest: false,
+			expected: Operation{
+				Consumes: nil,
+				Parameters: []*Parameter{
+					&Parameter{
+						In: "body",
+					},
+				},
+			},
+		},
+	}
+	expr.Root.API = &expr.APIExpr{
+		HTTP: &expr.HTTPExpr{
+			Path: "/",
+		},
+	}
+	for k, tc := range cases {
+		t.Run(k, func(t *testing.T) {
+			s := &V2{
+				Consumes: []string{"application/json"},
+				Paths:    make(map[string]interface{}),
+			}
+			root := &expr.RootExpr{
+				API: &expr.APIExpr{},
+			}
+			h := &expr.HostExpr{}
+			route := &expr.RouteExpr{
+				Method: "POST",
+				Endpoint: &expr.HTTPEndpointExpr{
+					MethodExpr: &expr.MethodExpr{
+						Payload: &expr.AttributeExpr{},
+					},
+					Service: &expr.HTTPServiceExpr{
+						ServiceExpr: &expr.ServiceExpr{},
+						Paths:       []string{"/foo"},
+						Params:      expr.NewEmptyMappedAttributeExpr(),
+					},
+					Headers: expr.NewEmptyMappedAttributeExpr(),
+					Body: &expr.AttributeExpr{
+						Type: expr.String,
+					},
+					MultipartRequest: tc.multipartRequest,
+				},
+			}
+			basePath := "/"
+			buildPathFromExpr(s, root, h, route, basePath)
+			for _, path := range s.Paths {
+				actual := path.(*Path).Post
+				if len(actual.Consumes) != len(tc.expected.Consumes) {
+					t.Errorf("expected the number of consumes to match %d got %d", len(actual.Consumes), len(tc.expected.Consumes))
+				} else {
+					for i, v := range actual.Consumes {
+						if v != tc.expected.Consumes[i] {
+							t.Errorf("got %#v, expected %#v at index %d", v, tc.expected.Consumes[i], i)
+						}
+					}
+				}
+				if len(actual.Parameters) != len(tc.expected.Parameters) {
+					t.Errorf("expected the number of parameters to match %d got %d", len(actual.Parameters), len(tc.expected.Parameters))
+				} else {
+					for i, v := range actual.Parameters {
+						if v.In != tc.expected.Parameters[i].In {
+							t.Errorf("got %#v, expected %#v at index %d", v.In, tc.expected.Parameters[i].In, i)
+						}
+					}
+				}
+			}
+		})
+	}
+}