From faebd2388955fa76a9f54bda3d4536a36a8cd11d Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Tue, 12 Oct 2021 11:28:53 +0200 Subject: [PATCH 1/2] reproduce issue #436 Signed-off-by: Pierre Fenoll --- openapi3filter/issue436_test.go | 105 ++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 openapi3filter/issue436_test.go diff --git a/openapi3filter/issue436_test.go b/openapi3filter/issue436_test.go new file mode 100644 index 000000000..56c5f820b --- /dev/null +++ b/openapi3filter/issue436_test.go @@ -0,0 +1,105 @@ +package openapi3filter + +import ( + "bytes" + "context" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/textproto" + "strings" + "testing" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/getkin/kin-openapi/routers/gorillamux" + "github.com/stretchr/testify/require" +) + +func TestIssue436(t *testing.T) { + const testSchema = ` +openapi: 3.0.0 +info: + title: 'Validator' + version: 0.0.1 +paths: + /test: + post: + requestBody: + required: true + content: + multipart/form-data: + #application/json: + schema: + type: object + required: + - file + properties: + file: + type: string + format: binary + categories: + type: array + items: + $ref: "#/components/schemas/Category" + responses: + '200': + description: Created + +components: + schemas: + Category: + type: object + properties: + name: + type: string + required: + - name +` + + doc, err := openapi3.NewLoader().LoadFromData([]byte(testSchema)) + require.NoError(t, err) + err = doc.Validate(context.Background()) + require.NoError(t, err) + + router, err := gorillamux.NewRouter(doc) + require.NoError(t, err) + + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"`, "categories")) + h.Set("Content-Type", "application/json") + fw, err := writer.CreatePart(h) + require.NoError(t, err) + _, err = io.Copy(fw, strings.NewReader(`[{"name": "foo"}]`)) + require.NoError(t, err) + + fw, err = writer.CreateFormFile("file", "hello.txt") + require.NoError(t, err) + _, err = io.Copy(fw, strings.NewReader("hello")) + require.NoError(t, err) + + writer.Close() + + req, _ := http.NewRequest(http.MethodPost, "/test", bytes.NewReader(body.Bytes())) + req.Header.Set("Content-Type", writer.FormDataContentType()) + + route, pathParams, _ := router.FindRoute(req) + + reqBody := route.Operation.RequestBody.Value + + requestValidationInput := &RequestValidationInput{ + Request: req, + PathParams: pathParams, + Route: route, + } + + err = ValidateRequestBody(context.TODO(), requestValidationInput, reqBody) + if err == nil { + fmt.Println("Valid") + } else { + fmt.Printf("NOT valid. %s\n", err) + } +} From aa385636817f379022d2362995c28a0d3eb340fb Mon Sep 17 00:00:00 2001 From: Pierre Fenoll Date: Tue, 12 Oct 2021 17:31:05 +0200 Subject: [PATCH 2/2] add as an example Signed-off-by: Pierre Fenoll --- openapi3filter/issue436_test.go | 104 ++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 37 deletions(-) diff --git a/openapi3filter/issue436_test.go b/openapi3filter/issue436_test.go index 56c5f820b..fa106c5a1 100644 --- a/openapi3filter/issue436_test.go +++ b/openapi3filter/issue436_test.go @@ -1,23 +1,21 @@ -package openapi3filter +package openapi3filter_test import ( "bytes" "context" - "fmt" "io" "mime/multipart" "net/http" "net/textproto" "strings" - "testing" "github.com/getkin/kin-openapi/openapi3" + "github.com/getkin/kin-openapi/openapi3filter" "github.com/getkin/kin-openapi/routers/gorillamux" - "github.com/stretchr/testify/require" ) -func TestIssue436(t *testing.T) { - const testSchema = ` +func Example_validateMultipartFormData() { + const spec = ` openapi: 3.0.0 info: title: 'Validator' @@ -29,7 +27,6 @@ paths: required: true content: multipart/form-data: - #application/json: schema: type: object required: @@ -57,49 +54,82 @@ components: - name ` - doc, err := openapi3.NewLoader().LoadFromData([]byte(testSchema)) - require.NoError(t, err) - err = doc.Validate(context.Background()) - require.NoError(t, err) + loader := openapi3.NewLoader() + doc, err := loader.LoadFromData([]byte(spec)) + if err != nil { + panic(err) + } + if err = doc.Validate(loader.Context); err != nil { + panic(err) + } router, err := gorillamux.NewRouter(doc) - require.NoError(t, err) + if err != nil { + panic(err) + } body := &bytes.Buffer{} writer := multipart.NewWriter(body) - h := make(textproto.MIMEHeader) - h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"`, "categories")) - h.Set("Content-Type", "application/json") - fw, err := writer.CreatePart(h) - require.NoError(t, err) - _, err = io.Copy(fw, strings.NewReader(`[{"name": "foo"}]`)) - require.NoError(t, err) + { // Add a single "categories" item as part data + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", `form-data; name="categories"`) + h.Set("Content-Type", "application/json") + fw, err := writer.CreatePart(h) + if err != nil { + panic(err) + } + if _, err = io.Copy(fw, strings.NewReader(`{"name": "foo"}`)); err != nil { + panic(err) + } + } + + { // Add a single "categories" item as part data, again + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", `form-data; name="categories"`) + h.Set("Content-Type", "application/json") + fw, err := writer.CreatePart(h) + if err != nil { + panic(err) + } + if _, err = io.Copy(fw, strings.NewReader(`{"name": "bar"}`)); err != nil { + panic(err) + } + } - fw, err = writer.CreateFormFile("file", "hello.txt") - require.NoError(t, err) - _, err = io.Copy(fw, strings.NewReader("hello")) - require.NoError(t, err) + { // Add file data + fw, err := writer.CreateFormFile("file", "hello.txt") + if err != nil { + panic(err) + } + if _, err = io.Copy(fw, strings.NewReader("hello")); err != nil { + panic(err) + } + } writer.Close() - req, _ := http.NewRequest(http.MethodPost, "/test", bytes.NewReader(body.Bytes())) + req, err := http.NewRequest(http.MethodPost, "/test", bytes.NewReader(body.Bytes())) + if err != nil { + panic(err) + } req.Header.Set("Content-Type", writer.FormDataContentType()) - route, pathParams, _ := router.FindRoute(req) - - reqBody := route.Operation.RequestBody.Value - - requestValidationInput := &RequestValidationInput{ - Request: req, - PathParams: pathParams, - Route: route, + route, pathParams, err := router.FindRoute(req) + if err != nil { + panic(err) } - err = ValidateRequestBody(context.TODO(), requestValidationInput, reqBody) - if err == nil { - fmt.Println("Valid") - } else { - fmt.Printf("NOT valid. %s\n", err) + if err = openapi3filter.ValidateRequestBody( + context.Background(), + &openapi3filter.RequestValidationInput{ + Request: req, + PathParams: pathParams, + Route: route, + }, + route.Operation.RequestBody.Value, + ); err != nil { + panic(err) } + // Output: }