From 8d540db3f8787395c876c3e612966d43bd98a6a1 Mon Sep 17 00:00:00 2001 From: yudppp Date: Mon, 2 Mar 2020 22:57:48 +0900 Subject: [PATCH] fix: Add Upload.ContentType test --- example/fileupload/fileupload_test.go | 117 +++++++++++--------- example/fileupload/generated.go | 54 ++++++++- example/fileupload/model/generated.go | 7 +- example/fileupload/schema.graphql | 1 + graphql/handler/transport/http_form_test.go | 70 +++++++----- graphql/handler_test.go | 21 ++-- 6 files changed, 177 insertions(+), 93 deletions(-) diff --git a/example/fileupload/fileupload_test.go b/example/fileupload/fileupload_test.go index 9856d639c1e..8d68252e009 100644 --- a/example/fileupload/fileupload_test.go +++ b/example/fileupload/fileupload_test.go @@ -10,6 +10,7 @@ import ( "mime/multipart" "net/http" "net/http/httptest" + "net/textproto" "testing" "github.com/99designs/gqlgen/example/fileupload/model" @@ -32,21 +33,23 @@ func TestFileUpload(t *testing.T) { require.Equal(t, string(content), "test") return &model.File{ - ID: 1, - Name: file.Filename, - Content: string(content), + ID: 1, + Name: file.Filename, + Content: string(content), + ContentType: file.ContentType, }, nil } srv := httptest.NewServer(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))) defer srv.Close() - operations := `{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id, name, content } }", "variables": { "file": null } }` + operations := `{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id, name, content, contentType } }", "variables": { "file": null } }` mapData := `{ "0": ["variables.file"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test", + mapKey: "0", + name: "a.txt", + content: "test", + contentType: "text/plain", }, } req := createUploadRequest(t, srv.URL, operations, mapData, files) @@ -57,7 +60,7 @@ func TestFileUpload(t *testing.T) { responseBody, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) responseString := string(responseBody) - require.Equal(t, `{"data":{"singleUpload":{"id":1,"name":"a.txt","content":"test"}}}`, responseString) + require.Equal(t, `{"data":{"singleUpload":{"id":1,"name":"a.txt","content":"test","contentType":"text/plain"}}}`, responseString) err = resp.Body.Close() require.Nil(t, err) }) @@ -73,21 +76,23 @@ func TestFileUpload(t *testing.T) { require.Equal(t, string(content), "test") return &model.File{ - ID: 1, - Name: req.File.Filename, - Content: string(content), + ID: 1, + Name: req.File.Filename, + Content: string(content), + ContentType: req.File.ContentType, }, nil } srv := httptest.NewServer(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))) defer srv.Close() - operations := `{ "query": "mutation ($req: UploadFile!) { singleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": {"file": null, "id": 1 } } }` + operations := `{ "query": "mutation ($req: UploadFile!) { singleUploadWithPayload(req: $req) { id, name, content, contentType } }", "variables": { "req": {"file": null, "id": 1 } } }` mapData := `{ "0": ["variables.req.file"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test", + mapKey: "0", + name: "a.txt", + content: "test", + contentType: "text/plain", }, } req := createUploadRequest(t, srv.URL, operations, mapData, files) @@ -97,7 +102,7 @@ func TestFileUpload(t *testing.T) { require.Equal(t, http.StatusOK, resp.StatusCode) responseBody, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) - require.Equal(t, `{"data":{"singleUploadWithPayload":{"id":1,"name":"a.txt","content":"test"}}}`, string(responseBody)) + require.Equal(t, `{"data":{"singleUploadWithPayload":{"id":1,"name":"a.txt","content":"test","contentType":"text/plain"}}}`, string(responseBody)) err = resp.Body.Close() require.Nil(t, err) }) @@ -114,9 +119,10 @@ func TestFileUpload(t *testing.T) { require.Nil(t, err) contents = append(contents, string(content)) resp = append(resp, &model.File{ - ID: i + 1, - Name: files[i].Filename, - Content: string(content), + ID: i + 1, + Name: files[i].Filename, + Content: string(content), + ContentType: files[i].ContentType, }) } require.ElementsMatch(t, []string{"test1", "test2"}, contents) @@ -125,18 +131,20 @@ func TestFileUpload(t *testing.T) { srv := httptest.NewServer(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))) defer srv.Close() - operations := `{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id, name, content } }", "variables": { "files": [null, null] } }` + operations := `{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id, name, content, contentType } }", "variables": { "files": [null, null] } }` mapData := `{ "0": ["variables.files.0"], "1": ["variables.files.1"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, { - mapKey: "1", - name: "b.txt", - content: "test2", + mapKey: "1", + name: "b.txt", + content: "test2", + contentType: "text/plain", }, } req := createUploadRequest(t, srv.URL, operations, mapData, files) @@ -146,7 +154,7 @@ func TestFileUpload(t *testing.T) { require.Equal(t, http.StatusOK, resp.StatusCode) responseBody, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) - require.Equal(t, `{"data":{"multipleUpload":[{"id":1,"name":"a.txt","content":"test1"},{"id":2,"name":"b.txt","content":"test2"}]}}`, string(responseBody)) + require.Equal(t, `{"data":{"multipleUpload":[{"id":1,"name":"a.txt","content":"test1","contentType":"text/plain"},{"id":2,"name":"b.txt","content":"test2","contentType":"text/plain"}]}}`, string(responseBody)) err = resp.Body.Close() require.Nil(t, err) }) @@ -166,9 +174,10 @@ func TestFileUpload(t *testing.T) { ids = append(ids, req[i].ID) contents = append(contents, string(content)) resp = append(resp, &model.File{ - ID: i + 1, - Name: req[i].File.Filename, - Content: string(content), + ID: i + 1, + Name: req[i].File.Filename, + Content: string(content), + ContentType: req[i].File.ContentType, }) } require.ElementsMatch(t, []int{1, 2}, ids) @@ -178,18 +187,20 @@ func TestFileUpload(t *testing.T) { srv := httptest.NewServer(handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))) defer srv.Close() - operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }` + operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content, contentType } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }` mapData := `{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, { - mapKey: "1", - name: "b.txt", - content: "test2", + mapKey: "1", + name: "b.txt", + content: "test2", + contentType: "text/plain", }, } req := createUploadRequest(t, srv.URL, operations, mapData, files) @@ -199,7 +210,7 @@ func TestFileUpload(t *testing.T) { require.Equal(t, http.StatusOK, resp.StatusCode) responseBody, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) - require.Equal(t, `{"data":{"multipleUploadWithPayload":[{"id":1,"name":"a.txt","content":"test1"},{"id":2,"name":"b.txt","content":"test2"}]}}`, string(responseBody)) + require.Equal(t, `{"data":{"multipleUploadWithPayload":[{"id":1,"name":"a.txt","content":"test1","contentType":"text/plain"},{"id":2,"name":"b.txt","content":"test2","contentType":"text/plain"}]}}`, string(responseBody)) err = resp.Body.Close() require.Nil(t, err) }) @@ -230,9 +241,10 @@ func TestFileUpload(t *testing.T) { } contents = append(contents, string(got)) resp = append(resp, &model.File{ - ID: i + 1, - Name: req[i].File.Filename, - Content: string(got), + ID: i + 1, + Name: req[i].File.Filename, + Content: string(got), + ContentType: req[i].File.ContentType, }) } require.ElementsMatch(t, []int{1, 2}, ids) @@ -240,13 +252,14 @@ func TestFileUpload(t *testing.T) { return resp, nil } - operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }` + operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content, contentType } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }` mapData := `{ "0": ["variables.req.0.file", "variables.req.1.file"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, } @@ -262,7 +275,7 @@ func TestFileUpload(t *testing.T) { require.Equal(t, http.StatusOK, resp.StatusCode) responseBody, err := ioutil.ReadAll(resp.Body) require.Nil(t, err) - require.Equal(t, `{"data":{"multipleUploadWithPayload":[{"id":1,"name":"a.txt","content":"test1"},{"id":2,"name":"a.txt","content":"test1"}]}}`, string(responseBody)) + require.Equal(t, `{"data":{"multipleUploadWithPayload":[{"id":1,"name":"a.txt","content":"test1","contentType":"text/plain"},{"id":2,"name":"a.txt","content":"test1","contentType":"text/plain"}]}}`, string(responseBody)) err = resp.Body.Close() require.Nil(t, err) } @@ -278,9 +291,10 @@ func TestFileUpload(t *testing.T) { } type file struct { - mapKey string - name string - content string + mapKey string + name string + content string + contentType string } func createUploadRequest(t *testing.T, url, operations, mapData string, files []file) *http.Request { @@ -294,7 +308,10 @@ func createUploadRequest(t *testing.T, url, operations, mapData string, files [] require.NoError(t, err) for i := range files { - ff, err := bodyWriter.CreateFormFile(files[i].mapKey, files[i].name) + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, files[i].mapKey, files[i].name)) + h.Set("Content-Type", files[i].contentType) + ff, err := bodyWriter.CreatePart(h) require.NoError(t, err) _, err = ff.Write([]byte(files[i].content)) require.NoError(t, err) diff --git a/example/fileupload/generated.go b/example/fileupload/generated.go index 125846e6dfd..4564bf52dbb 100644 --- a/example/fileupload/generated.go +++ b/example/fileupload/generated.go @@ -44,9 +44,10 @@ type DirectiveRoot struct { type ComplexityRoot struct { File struct { - Content func(childComplexity int) int - ID func(childComplexity int) int - Name func(childComplexity int) int + Content func(childComplexity int) int + ContentType func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int } Mutation struct { @@ -93,6 +94,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.File.Content(childComplexity), true + case "File.contentType": + if e.complexity.File.ContentType == nil { + break + } + + return e.complexity.File.ContentType(childComplexity), true + case "File.id": if e.complexity.File.ID == nil { break @@ -234,6 +242,7 @@ type File { id: Int! name: String! content: String! + contentType: String! } "The ` + "`" + `UploadFile` + "`" + ` type, represents the request for uploading a file with certain payload." @@ -471,6 +480,40 @@ func (ec *executionContext) _File_content(ctx context.Context, field graphql.Col return ec.marshalNString2string(ctx, field.Selections, res) } +func (ec *executionContext) _File_contentType(ctx context.Context, field graphql.CollectedField, obj *model.File) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "File", + Field: field, + Args: nil, + IsMethod: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ContentType, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + fc.Result = res + return ec.marshalNString2string(ctx, field.Selections, res) +} + func (ec *executionContext) _Mutation_singleUpload(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -1851,6 +1894,11 @@ func (ec *executionContext) _File(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { invalids++ } + case "contentType": + out.Values[i] = ec._File_contentType(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } diff --git a/example/fileupload/model/generated.go b/example/fileupload/model/generated.go index c311e9adfe9..149dcd557e4 100644 --- a/example/fileupload/model/generated.go +++ b/example/fileupload/model/generated.go @@ -8,9 +8,10 @@ import ( // The `File` type, represents the response of uploading a file. type File struct { - ID int `json:"id"` - Name string `json:"name"` - Content string `json:"content"` + ID int `json:"id"` + Name string `json:"name"` + Content string `json:"content"` + ContentType string `json:"contentType"` } // The `UploadFile` type, represents the request for uploading a file with certain payload. diff --git a/example/fileupload/schema.graphql b/example/fileupload/schema.graphql index 891c61ec93b..f4ded39c9b7 100644 --- a/example/fileupload/schema.graphql +++ b/example/fileupload/schema.graphql @@ -6,6 +6,7 @@ type File { id: Int! name: String! content: String! + contentType: String! } "The `UploadFile` type, represents the request for uploading a file with certain payload." diff --git a/graphql/handler/transport/http_form_test.go b/graphql/handler/transport/http_form_test.go index 0a2d5c006a3..944566bc39e 100644 --- a/graphql/handler/transport/http_form_test.go +++ b/graphql/handler/transport/http_form_test.go @@ -3,10 +3,12 @@ package transport_test import ( "bytes" "context" + "fmt" "io/ioutil" "mime/multipart" "net/http" "net/http/httptest" + "net/textproto" "testing" "github.com/99designs/gqlgen/graphql" @@ -52,9 +54,10 @@ func TestFileUpload(t *testing.T) { mapData := `{ "0": ["variables.file"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, } req := createUploadRequest(t, operations, mapData, files) @@ -77,9 +80,10 @@ func TestFileUpload(t *testing.T) { mapData := `{ "0": ["variables.req.file"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, } req := createUploadRequest(t, operations, mapData, files) @@ -102,14 +106,16 @@ func TestFileUpload(t *testing.T) { mapData := `{ "0": ["variables.files.0"], "1": ["variables.files.1"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, { - mapKey: "1", - name: "b.txt", - content: "test2", + mapKey: "1", + name: "b.txt", + content: "test2", + contentType: "text/plain", }, } req := createUploadRequest(t, operations, mapData, files) @@ -132,14 +138,16 @@ func TestFileUpload(t *testing.T) { mapData := `{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, { - mapKey: "1", - name: "b.txt", - content: "test2", + mapKey: "1", + name: "b.txt", + content: "test2", + contentType: "text/plain", }, } req := createUploadRequest(t, operations, mapData, files) @@ -164,9 +172,10 @@ func TestFileUpload(t *testing.T) { mapData := `{ "0": ["variables.req.0.file", "variables.req.1.file"] }` files := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, } req := createUploadRequest(t, operations, mapData, files) @@ -190,9 +199,10 @@ func TestFileUpload(t *testing.T) { validMap := `{ "0": ["variables.file"] }` validFiles := []file{ { - mapKey: "0", - name: "a.txt", - content: "test1", + mapKey: "0", + name: "a.txt", + content: "test1", + contentType: "text/plain", }, } @@ -260,9 +270,10 @@ func TestFileUpload(t *testing.T) { } type file struct { - mapKey string - name string - content string + mapKey string + name string + content string + contentType string } func createUploadRequest(t *testing.T, operations, mapData string, files []file) *http.Request { @@ -276,7 +287,10 @@ func createUploadRequest(t *testing.T, operations, mapData string, files []file) require.NoError(t, err) for i := range files { - ff, err := bodyWriter.CreateFormFile(files[i].mapKey, files[i].name) + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, files[i].mapKey, files[i].name)) + h.Set("Content-Type", files[i].contentType) + ff, err := bodyWriter.CreatePart(h) require.NoError(t, err) _, err = ff.Write([]byte(files[i].content)) require.NoError(t, err) diff --git a/graphql/handler_test.go b/graphql/handler_test.go index 1caf965b9b7..3ba421f3920 100644 --- a/graphql/handler_test.go +++ b/graphql/handler_test.go @@ -15,9 +15,10 @@ func TestAddUploadToOperations(t *testing.T) { params := &RawParams{} upload := Upload{ - File: file, - Filename: "a.txt", - Size: int64(5), + File: file, + Filename: "a.txt", + Size: int64(5), + ContentType: "text/plain", } path := "variables.req.0.file" err := params.AddUpload(upload, key, path) @@ -34,9 +35,10 @@ func TestAddUploadToOperations(t *testing.T) { } upload := Upload{ - File: file, - Filename: "a.txt", - Size: int64(5), + File: file, + Filename: "a.txt", + Size: int64(5), + ContentType: "text/plain", } expected := &RawParams{ @@ -65,9 +67,10 @@ func TestAddUploadToOperations(t *testing.T) { } upload := Upload{ - File: file, - Filename: "a.txt", - Size: int64(5), + File: file, + Filename: "a.txt", + Size: int64(5), + ContentType: "text/plain", } expected := &RawParams{