-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Example file service * Parse multipart request * Inject files to values * Finalize upload integration * Add README.md * Add multi upload endpoint * Fix multiple file upload * Add multi upload instructions * Add batch operations instructions * Update branch dependency * Accept empty content-type header * Reorganise request parsing * Add test for positive scenarios * Some negative tests * Convert negative tests to table tests * Drop todo in example's todo * Update dependecy to the main library
- Loading branch information
Showing
10 changed files
with
1,015 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# File uploads | ||
|
||
This example demonstrates proxying file upload requests according to specification: | ||
- https://github.com/jaydenseric/graphql-multipart-request-spec | ||
|
||
## How to test | ||
|
||
1. Run file upload service | ||
|
||
``` | ||
cd examples/fileupload/ | ||
go run serviceUpload.go | ||
``` | ||
|
||
2. Run the gateway | ||
``` | ||
go run ./cmd/ start --port 4000 --services http://localhost:5000 | ||
``` | ||
|
||
3. Execute file upload query: | ||
|
||
``` | ||
curl localhost:4000/graphql \ | ||
-F operations='{ "query": "mutation ($someFile: Upload!) { upload(file: $someFile) }", "variables": { "someFile": null } }' \ | ||
-F map='{ "0": ["variables.someFile"] }' \ | ||
-F [email protected] | ||
``` | ||
|
||
4. Validate that the file is uploaded to temporary folder: | ||
|
||
``` | ||
ls examples/fileupload/tmp/ | ||
> cd4c0810-d5d7-4adf-9edb-bea74eadae4e | ||
head -n3 examples/fileupload/tmp/* | ||
> # nautilus/gateway | ||
> | ||
> ![CI Checks](https://github.com/nautilus/gateway/workflows/CI%20Checks/badge.svg?branch=master) [![Coverage Status](https://coveralls.io/repos/github/nautilus/gateway/badge.svg?branch=master)](https://coveralls.io/github/nautilus/gateway?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/nautilus/gateway)](https://goreportcard.com/report/github.com/nautilus/gateway) | ||
``` | ||
|
||
5. Execute multi file upload query: | ||
``` | ||
curl localhost:4000/graphql \ | ||
-F operations='{"query":"mutation TestFileUpload(\n $someFile: Upload!,\n\t$allFiles: [Upload!]!\n) {\n upload(file: $someFile)\n uploadMulti(files: $allFiles)\n}","variables":{"someFile":null,"allFiles":[null,null]},"operationName":"TestFileUpload"}' \ | ||
-F map='{"0":["variables.someFile"],"1":["variables.allFiles.0"],"2":["variables.allFiles.1"]}' \ | ||
-F [email protected] \ | ||
-F [email protected] \ | ||
-F [email protected] | ||
``` | ||
|
||
6. Validate that more files are created in the folder: | ||
|
||
``` | ||
ls -la examples/fileupload/tmp/ | ||
> -rw-rw-r-- 1 user user 924 Sep 3 00:12 343b9067-f2be-4ea9-b73b-4e8390ed55c7 | ||
> -rw-rw-r-- 1 user user 1557 Sep 3 00:12 5417f766-8e7d-44ef-afb6-90ec0b4c548c | ||
> -rw-rw-r-- 1 user user 15089 Sep 3 00:12 a590196f-6450-4785-8998-8013ff7c8cf3 | ||
``` | ||
|
||
7. Testing batch mode | ||
|
||
``` | ||
curl localhost:4000/graphql \ | ||
-F operations='[{"query":"mutation ($someFile: Upload!) { upload(file: $someFile) }","variables":{"someFile":null}}, {"query":"mutation TestFileUpload(\n $someFile: Upload!,\n\t$allFiles: [Upload!]!\n) {\n upload(file: $someFile)\n uploadMulti(files: $allFiles)\n}","variables":{"someFile":null,"allFiles":[null,null]},"operationName":"TestFileUpload"}]' \ | ||
-F map='{"0":["0.variables.someFile"],"1":["1.variables.someFile"],"2":["1.variables.allFiles.0"],"3":["1.variables.allFiles.1"]}' \ | ||
-F [email protected] \ | ||
-F [email protected] \ | ||
-F [email protected] \ | ||
-F [email protected] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module github.com/nautilus/gateway/example/fileupload | ||
|
||
go 1.14 | ||
|
||
require ( | ||
github.com/graphql-go/graphql v0.7.9 | ||
github.com/jpascal/graphql-upload v0.0.0-20200219114743-2a693c100233 | ||
github.com/satori/go.uuid v1.2.0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
github.com/graphql-go/graphql v0.7.9 h1:5Va/Rt4l5g3YjwDnid3vFfn43faaQBq7rMcIZ0VnV34= | ||
github.com/graphql-go/graphql v0.7.9/go.mod h1:k6yrAYQaSP59DC5UVxbgxESlmVyojThKdORUqGDGmrI= | ||
github.com/jpascal/graphql-upload v0.0.0-20200219114743-2a693c100233 h1:6tY2KAHlytnGu9s7ceFZURenjqmxbVHwGembFi6sz+c= | ||
github.com/jpascal/graphql-upload v0.0.0-20200219114743-2a693c100233/go.mod h1:I/WT/Xwt4isoRE43Sr1LG8T0bGkxmYEaQRvKbXSdVSo= | ||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= | ||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/sha1" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
"github.com/graphql-go/graphql" | ||
handler "github.com/jpascal/graphql-upload" | ||
uuid "github.com/satori/go.uuid" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"net/http" | ||
"os" | ||
"path" | ||
) | ||
|
||
var UploadType = graphql.NewScalar(graphql.ScalarConfig{ | ||
Name: "Upload", | ||
Description: "Scalar upload object", | ||
}) | ||
|
||
type FileWrapper struct { | ||
File *os.File | ||
Name string | ||
} | ||
|
||
var File = graphql.NewObject(graphql.ObjectConfig{ | ||
Name: "File", | ||
Description: "File object", | ||
Fields: graphql.Fields{ | ||
"name": &graphql.Field{ | ||
Type: graphql.String, | ||
Resolve: func(params graphql.ResolveParams) (interface{}, error) { | ||
file := params.Source.(*FileWrapper) | ||
name := path.Base(file.Name) | ||
|
||
return name, nil | ||
}, | ||
}, | ||
"hash": &graphql.Field{ | ||
Type: graphql.String, | ||
Resolve: func(params graphql.ResolveParams) (interface{}, error) { | ||
file := params.Source.(*FileWrapper) | ||
if data, err := ioutil.ReadAll(file.File); err == nil { | ||
fileHash := sha1.Sum(data) | ||
|
||
return hex.EncodeToString(fileHash[:]), nil | ||
} else { | ||
return nil, err | ||
} | ||
|
||
}, | ||
}, | ||
"size": &graphql.Field{ | ||
Type: graphql.Int, | ||
Resolve: func(params graphql.ResolveParams) (interface{}, error) { | ||
file := params.Source.(*FileWrapper) | ||
if info, err := file.File.Stat(); err != nil { | ||
return nil, err | ||
} else { | ||
return info.Size(), nil | ||
} | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
func main() { | ||
schema, err := graphql.NewSchema( | ||
graphql.SchemaConfig{ | ||
Query: graphql.NewObject( | ||
graphql.ObjectConfig{ | ||
Name: "Query", | ||
Fields: graphql.Fields{ | ||
"file": &graphql.Field{ | ||
Type: File, | ||
Args: graphql.FieldConfigArgument{ | ||
"id": &graphql.ArgumentConfig{ | ||
Type: graphql.NewNonNull(graphql.String), | ||
}, | ||
}, | ||
Resolve: func(params graphql.ResolveParams) (interface{}, error) { | ||
if fileId, ok := params.Args["id"].(string); ok { | ||
fileUuid, err := uuid.FromString(fileId) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
file, err := os.Open("tmp/" + fileUuid.String()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &FileWrapper{File: file, Name: fileUuid.String()}, nil | ||
} else { | ||
return nil, errors.New("file id is not provided") | ||
} | ||
}, | ||
}, | ||
}, | ||
}), | ||
Mutation: graphql.NewObject( | ||
graphql.ObjectConfig{ | ||
Name: "Mutation", | ||
Fields: graphql.Fields{ | ||
"upload": &graphql.Field{ | ||
Type: graphql.NewNonNull(graphql.String), | ||
Args: graphql.FieldConfigArgument{ | ||
"file": &graphql.ArgumentConfig{ | ||
Type: graphql.NewNonNull(UploadType), | ||
}, | ||
}, | ||
Resolve: func(params graphql.ResolveParams) (interface{}, error) { | ||
upload, uploadPresent := params.Args["file"].(handler.File) | ||
if uploadPresent { | ||
id := uuid.NewV4().String() | ||
targetFile, err := os.Create("tmp/" + id) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
defer targetFile.Close() | ||
nBytes, err := io.Copy(targetFile, upload.File) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
log.Println("File saved nBytes: ", nBytes) | ||
return id, nil | ||
} else { | ||
return nil, errors.New("no file found in request") | ||
} | ||
|
||
}, | ||
}, | ||
"uploadMulti": &graphql.Field{ | ||
Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(graphql.String))), | ||
Args: graphql.FieldConfigArgument{ | ||
"files": &graphql.ArgumentConfig{ | ||
Type: graphql.NewNonNull(graphql.NewList(graphql.NewNonNull(UploadType))), | ||
}, | ||
}, | ||
Resolve: func(params graphql.ResolveParams) (interface{}, error) { | ||
uploads, uploadPresent := params.Args["files"].([]interface{}) | ||
if uploadPresent { | ||
var result []string | ||
for i, uploadItem := range uploads { | ||
|
||
upload, ok := uploadItem.(handler.File) | ||
if !ok { | ||
return nil, errors.New(fmt.Sprintf("type of file %d is wrong", i)) | ||
} | ||
|
||
id := uuid.NewV4().String() | ||
targetFile, err := os.Create("tmp/" + id) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
defer targetFile.Close() | ||
nBytes, err := io.Copy(targetFile, upload.File) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
log.Println("File saved nBytes: ", nBytes) | ||
result = append(result, id) | ||
} | ||
|
||
return result, nil | ||
} else { | ||
return nil, errors.New("no file found in request") | ||
} | ||
|
||
}, | ||
}, | ||
}, | ||
}), | ||
}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
server := &http.Server{Addr: "0.0.0.0:5000", Handler: handler.New(func(request *handler.Request) interface{} { | ||
return graphql.Do(graphql.Params{ | ||
RequestString: request.Query, | ||
OperationName: request.OperationName, | ||
VariableValues: request.Variables, | ||
Schema: schema, | ||
Context: request.Context, | ||
}) | ||
}, &handler.Config{ | ||
MaxBodySize: 1024, | ||
}), | ||
} | ||
server.ListenAndServe() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
* | ||
!.gitignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.