diff --git a/codegen/config/config.go b/codegen/config/config.go index 502422de44..556ecef7ed 100644 --- a/codegen/config/config.go +++ b/codegen/config/config.go @@ -363,9 +363,10 @@ func (c *Config) InjectBuiltins(s *ast.Schema) { // These are additional types that are injected if defined in the schema as scalars. extraBuiltins := TypeMap{ - "Time": {Model: StringList{"github.com/99designs/gqlgen/graphql.Time"}}, - "Map": {Model: StringList{"github.com/99designs/gqlgen/graphql.Map"}}, - "Any": {Model: StringList{"github.com/99designs/gqlgen/graphql.Any"}}, + "Time": {Model: StringList{"github.com/99designs/gqlgen/graphql.Time"}}, + "Map": {Model: StringList{"github.com/99designs/gqlgen/graphql.Map"}}, + "Upload": {Model: StringList{"github.com/99designs/gqlgen/graphql.Upload"}}, + "Any": {Model: StringList{"github.com/99designs/gqlgen/graphql.Any"}}, } for typeName, entry := range extraBuiltins { diff --git a/docs/content/reference/file-upload.md b/docs/content/reference/file-upload.md new file mode 100644 index 0000000000..116bc4f7a4 --- /dev/null +++ b/docs/content/reference/file-upload.md @@ -0,0 +1,137 @@ +--- +title: 'File Upload' +description: How to upload files. +linkTitle: File Upload +menu: { main: { parent: 'reference' } } +--- + +Graphql server has an already built-in Upload scalar to upload files using a multipart request. \ +It implements the following spec https://github.com/jaydenseric/graphql-multipart-request-spec, +that defines an interoperable multipart form field structure for GraphQL requests, used by +various file upload client implementations. + +To use it you need to add the Upload scalar in your schema, and it will automatically add the +marshalling behaviour to Go types. + +# Configuration +There are two specific options that can be configured for uploading files: +- uploadMaxSize \ +This option specifies the maximum number of bytes used to parse a request body as multipart/form-data. +- uploadMaxMemory \ +This option specifies the maximum number of bytes used to parse a request body as +multipart/form-data in memory, with the remainder stored on disk in temporary files. + +# Examples + +## Single file upload +For this use case, the schema could look like this. + +```graphql +"The `UploadFile, // b.txt` scalar type represents a multipart file upload." +scalar Upload + +"The `Query` type, represents all of the entry points into our object graph." +type Query { + ... +} + +"The `Mutation` type, represents all updates we can make to our data." +type Mutation { + singleUpload(file: Upload!): Bool! +} +``` + +cURL can be used the make a query as follows: +``` +curl localhost:4000/graphql \ + -F operations='{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }' \ + -F map='{ "0": ["variables.file"] }' \ + -F 0=@a.txt +``` + +That invokes the following operation: +``` +{ + query: ` + mutation($file: Upload!) { + singleUpload(file: $file) { + id + } + } + `, + variables: { + file: File // a.txt + } +} +``` + +## Multiple file upload +For this use case, the schema could look like this. + +```graphql +"The `Upload` scalar type represents a multipart file upload." +scalar Upload + +"The `File` type, represents the response of uploading a file." +type File { + id: Int! + name: String! + content: String! +} + +"The `UploadFile` type, represents the request for uploading a file with a certain payload." +input UploadFile { + id: Int! + file: Upload! +} + +"The `Query` type, represents all of the entry points into our object graph." +type Query { + ... +} + +"The `Mutation` type, represents all updates we can make to our data." +type Mutation { + multipleUpload(req: [UploadFile!]!): [File!]! +} + +``` + +cURL can be used the make a query as follows: + +``` +curl localhost:4000/query \ + -F operations='{ "query": "mutation($req: [UploadFile!]!) { multipleUpload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }' \ + -F map='{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] }' \ + -F 0=@b.txt \ + -F 1=@c.txt +``` + +That invokes the following operation: +``` +{ + query: ` + mutation($req: [UploadFile!]!) + multipleUpload(req: $req) { + id, + name, + content + } + } + `, + variables: { + req: [ + { + id: 1, + File, // b.txt + }, + { + id: 2, + File, // c.txt + } + ] + } +} +``` + +see the [example/fileupload](https://github.com/99designs/gqlgen/tree/master/example/fileupload) package for more examples. diff --git a/docs/content/reference/scalars.md b/docs/content/reference/scalars.md index 0ac35d4d53..0cfd3355de 100644 --- a/docs/content/reference/scalars.md +++ b/docs/content/reference/scalars.md @@ -7,7 +7,7 @@ menu: { main: { parent: 'reference' } } ## Built-in helpers -gqlgen ships with three built-in helpers for common custom scalar use-cases, `Time`, `Any` and `Map`. Adding any of these to a schema will automatically add the marshalling behaviour to Go types. +gqlgen ships with three built-in helpers for common custom scalar use-cases, `Time`, `Any`, `Upload` and `Map`. Adding any of these to a schema will automatically add the marshalling behaviour to Go types. ### Time @@ -25,6 +25,19 @@ scalar Map Maps an arbitrary GraphQL value to a `map[string]{interface}` Go type. + +```graphql +scalar Upload +``` + +Maps a `Upload` GraphQL scalar to a `graphql.Upload` struct, defined as follows: +``` +type Upload struct { + File io.Reader + Filename string + Size int64 +} +``` ### Any ```graphql diff --git a/example/fileupload/.gqlgen.yml b/example/fileupload/.gqlgen.yml new file mode 100644 index 0000000000..b71c035a38 --- /dev/null +++ b/example/fileupload/.gqlgen.yml @@ -0,0 +1,2 @@ +model: + filename: model/generated.go diff --git a/example/fileupload/fileupload_test.go b/example/fileupload/fileupload_test.go new file mode 100644 index 0000000000..a23e2f2e12 --- /dev/null +++ b/example/fileupload/fileupload_test.go @@ -0,0 +1,311 @@ +package fileupload + +import ( + "bytes" + "context" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "net/http/httptest" + "testing" + + "github.com/99designs/gqlgen/example/fileupload/model" + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/handler" + "github.com/stretchr/testify/require" +) + +func TestFileUpload(t *testing.T) { + client := http.Client{} + + t.Run("valid single file upload", func(t *testing.T) { + resolver := &Resolver{ + SingleUploadFunc: func(ctx context.Context, file graphql.Upload) (*model.File, error) { + require.NotNil(t, file) + require.NotNil(t, file.File) + content, err := ioutil.ReadAll(file.File) + require.Nil(t, err) + require.Equal(t, string(content), "test") + + return &model.File{ + ID: 1, + Name: file.Filename, + Content: string(content), + }, nil + }, + } + srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolver}))) + defer srv.Close() + + operations := `{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id, name, content } }", "variables": { "file": null } }` + mapData := `{ "0": ["variables.file"] }` + files := []file{ + { + mapKey: "0", + name: "a.txt", + content: "test", + }, + } + req := createUploadRequest(t, srv.URL, operations, mapData, files) + + resp, err := client.Do(req) + require.Nil(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + 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) + err = resp.Body.Close() + require.Nil(t, err) + }) + + t.Run("valid single file upload with payload", func(t *testing.T) { + resolver := &Resolver{ + SingleUploadWithPayloadFunc: func(ctx context.Context, req model.UploadFile) (*model.File, error) { + require.Equal(t, req.ID, 1) + require.NotNil(t, req.File) + require.NotNil(t, req.File.File) + content, err := ioutil.ReadAll(req.File.File) + require.Nil(t, err) + require.Equal(t, string(content), "test") + + return &model.File{ + ID: 1, + Name: req.File.Filename, + Content: string(content), + }, nil + }, + } + srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolver}))) + defer srv.Close() + + operations := `{ "query": "mutation ($req: UploadFile!) { singleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": {"file": null, "id": 1 } } }` + mapData := `{ "0": ["variables.req.file"] }` + files := []file{ + { + mapKey: "0", + name: "a.txt", + content: "test", + }, + } + req := createUploadRequest(t, srv.URL, operations, mapData, files) + + resp, err := client.Do(req) + require.Nil(t, err) + 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)) + err = resp.Body.Close() + require.Nil(t, err) + }) + + t.Run("valid file list upload", func(t *testing.T) { + resolver := &Resolver{ + MultipleUploadFunc: func(ctx context.Context, files []graphql.Upload) ([]model.File, error) { + require.Len(t, files, 2) + var contents []string + var resp []model.File + for i := range files { + require.NotNil(t, files[i].File) + content, err := ioutil.ReadAll(files[i].File) + require.Nil(t, err) + contents = append(contents, string(content)) + resp = append(resp, model.File{ + ID: i + 1, + Name: files[i].Filename, + Content: string(content), + }) + } + require.ElementsMatch(t, []string{"test1", "test2"}, contents) + return resp, nil + }, + } + srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolver}))) + defer srv.Close() + + operations := `{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id, name, content } }", "variables": { "files": [null, null] } }` + mapData := `{ "0": ["variables.files.0"], "1": ["variables.files.1"] }` + files := []file{ + { + mapKey: "0", + name: "a.txt", + content: "test1", + }, + { + mapKey: "1", + name: "b.txt", + content: "test2", + }, + } + req := createUploadRequest(t, srv.URL, operations, mapData, files) + + resp, err := client.Do(req) + require.Nil(t, err) + 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)) + err = resp.Body.Close() + require.Nil(t, err) + }) + + t.Run("valid file list upload with payload", func(t *testing.T) { + resolver := &Resolver{ + MultipleUploadWithPayloadFunc: func(ctx context.Context, req []model.UploadFile) ([]model.File, error) { + require.Len(t, req, 2) + var ids []int + var contents []string + var resp []model.File + for i := range req { + require.NotNil(t, req[i].File) + require.NotNil(t, req[i].File.File) + content, err := ioutil.ReadAll(req[i].File.File) + require.Nil(t, err) + 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), + }) + } + require.ElementsMatch(t, []int{1, 2}, ids) + require.ElementsMatch(t, []string{"test1", "test2"}, contents) + return resp, nil + }, + } + srv := httptest.NewServer(handler.GraphQL(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 } ] } }` + mapData := `{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] }` + files := []file{ + { + mapKey: "0", + name: "a.txt", + content: "test1", + }, + { + mapKey: "1", + name: "b.txt", + content: "test2", + }, + } + req := createUploadRequest(t, srv.URL, operations, mapData, files) + + resp, err := client.Do(req) + require.Nil(t, err) + 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)) + err = resp.Body.Close() + require.Nil(t, err) + }) + + t.Run("valid file list upload with payload and file reuse", func(t *testing.T) { + resolver := &Resolver{ + MultipleUploadWithPayloadFunc: func(ctx context.Context, req []model.UploadFile) ([]model.File, error) { + require.Len(t, req, 2) + var ids []int + var contents []string + var resp []model.File + for i := range req { + require.NotNil(t, req[i].File) + require.NotNil(t, req[i].File.File) + ids = append(ids, req[i].ID) + + var got []byte + buf := make([]byte, 2) + for { + n, err := req[i].File.File.Read(buf) + got = append(got, buf[:n]...) + if err != nil { + if err == io.EOF { + break + } + require.Fail(t, "unexpected error while reading", err.Error()) + } + } + contents = append(contents, string(got)) + resp = append(resp, model.File{ + ID: i + 1, + Name: req[i].File.Filename, + Content: string(got), + }) + } + require.ElementsMatch(t, []int{1, 2}, ids) + require.ElementsMatch(t, []string{"test1", "test1"}, contents) + return resp, nil + }, + } + + operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content } }", "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", + }, + } + + test := func(uploadMaxMemory int64) { + memory := handler.UploadMaxMemory(uploadMaxMemory) + srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(Config{Resolvers: resolver}), memory)) + defer srv.Close() + req := createUploadRequest(t, srv.URL, operations, mapData, files) + resp, err := client.Do(req) + require.Nil(t, err) + 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)) + err = resp.Body.Close() + require.Nil(t, err) + } + + t.Run("payload smaller than UploadMaxMemory, stored in memory", func(t *testing.T) { + test(5000) + }) + + t.Run("payload bigger than UploadMaxMemory, persisted to disk", func(t *testing.T) { + test(2) + }) + }) +} + +type file struct { + mapKey string + name string + content string +} + +func createUploadRequest(t *testing.T, url, operations, mapData string, files []file) *http.Request { + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + + err := bodyWriter.WriteField("operations", operations) + require.NoError(t, err) + + err = bodyWriter.WriteField("map", mapData) + require.NoError(t, err) + + for i := range files { + ff, err := bodyWriter.CreateFormFile(files[i].mapKey, files[i].name) + require.NoError(t, err) + _, err = ff.Write([]byte(files[i].content)) + require.NoError(t, err) + } + err = bodyWriter.Close() + require.NoError(t, err) + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/graphql", url), bodyBuf) + require.NoError(t, err) + + req.Header.Set("Content-Type", bodyWriter.FormDataContentType()) + return req +} diff --git a/example/fileupload/generated.go b/example/fileupload/generated.go new file mode 100644 index 0000000000..b41d741ffc --- /dev/null +++ b/example/fileupload/generated.go @@ -0,0 +1,2491 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package fileupload + +import ( + "bytes" + "context" + "errors" + "strconv" + "sync" + + "github.com/99designs/gqlgen/example/fileupload/model" + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/graphql/introspection" + "github.com/vektah/gqlparser" + "github.com/vektah/gqlparser/ast" +) + +// region ************************** generated!.gotpl ************************** + +// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. +func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { + return &executableSchema{ + resolvers: cfg.Resolvers, + directives: cfg.Directives, + complexity: cfg.Complexity, + } +} + +type Config struct { + Resolvers ResolverRoot + Directives DirectiveRoot + Complexity ComplexityRoot +} + +type ResolverRoot interface { + Mutation() MutationResolver + Query() QueryResolver +} + +type DirectiveRoot struct { +} + +type ComplexityRoot struct { + File struct { + Content func(childComplexity int) int + ID func(childComplexity int) int + Name func(childComplexity int) int + } + + Mutation struct { + MultipleUpload func(childComplexity int, files []graphql.Upload) int + MultipleUploadWithPayload func(childComplexity int, req []model.UploadFile) int + SingleUpload func(childComplexity int, file graphql.Upload) int + SingleUploadWithPayload func(childComplexity int, req model.UploadFile) int + } + + Query struct { + Empty func(childComplexity int) int + } +} + +type MutationResolver interface { + SingleUpload(ctx context.Context, file graphql.Upload) (*model.File, error) + SingleUploadWithPayload(ctx context.Context, req model.UploadFile) (*model.File, error) + MultipleUpload(ctx context.Context, files []graphql.Upload) ([]model.File, error) + MultipleUploadWithPayload(ctx context.Context, req []model.UploadFile) ([]model.File, error) +} +type QueryResolver interface { + Empty(ctx context.Context) (string, error) +} + +type executableSchema struct { + resolvers ResolverRoot + directives DirectiveRoot + complexity ComplexityRoot +} + +func (e *executableSchema) Schema() *ast.Schema { + return parsedSchema +} + +func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { + ec := executionContext{nil, e} + _ = ec + switch typeName + "." + field { + + case "File.Content": + if e.complexity.File.Content == nil { + break + } + + return e.complexity.File.Content(childComplexity), true + + case "File.ID": + if e.complexity.File.ID == nil { + break + } + + return e.complexity.File.ID(childComplexity), true + + case "File.Name": + if e.complexity.File.Name == nil { + break + } + + return e.complexity.File.Name(childComplexity), true + + case "Mutation.MultipleUpload": + if e.complexity.Mutation.MultipleUpload == nil { + break + } + + args, err := ec.field_Mutation_multipleUpload_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.MultipleUpload(childComplexity, args["files"].([]graphql.Upload)), true + + case "Mutation.MultipleUploadWithPayload": + if e.complexity.Mutation.MultipleUploadWithPayload == nil { + break + } + + args, err := ec.field_Mutation_multipleUploadWithPayload_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.MultipleUploadWithPayload(childComplexity, args["req"].([]model.UploadFile)), true + + case "Mutation.SingleUpload": + if e.complexity.Mutation.SingleUpload == nil { + break + } + + args, err := ec.field_Mutation_singleUpload_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.SingleUpload(childComplexity, args["file"].(graphql.Upload)), true + + case "Mutation.SingleUploadWithPayload": + if e.complexity.Mutation.SingleUploadWithPayload == nil { + break + } + + args, err := ec.field_Mutation_singleUploadWithPayload_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.SingleUploadWithPayload(childComplexity, args["req"].(model.UploadFile)), true + + case "Query.Empty": + if e.complexity.Query.Empty == nil { + break + } + + return e.complexity.Query.Empty(childComplexity), true + + } + return 0, false +} + +func (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + ec := executionContext{graphql.GetRequestContext(ctx), e} + + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Query(ctx, op.SelectionSet) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) + + return &graphql.Response{ + Data: buf, + Errors: ec.Errors, + Extensions: ec.Extensions, + } +} + +func (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + ec := executionContext{graphql.GetRequestContext(ctx), e} + + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._Mutation(ctx, op.SelectionSet) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) + + return &graphql.Response{ + Data: buf, + Errors: ec.Errors, + Extensions: ec.Extensions, + } +} + +func (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDefinition) func() *graphql.Response { + return graphql.OneShot(graphql.ErrorResponse(ctx, "subscriptions are not supported")) +} + +type executionContext struct { + *graphql.RequestContext + *executableSchema +} + +func (ec *executionContext) FieldMiddleware(ctx context.Context, obj interface{}, next graphql.Resolver) (ret interface{}) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + res, err := ec.ResolverMiddleware(ctx, next) + if err != nil { + ec.Error(ctx, err) + return nil + } + return res +} + +func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapSchema(parsedSchema), nil +} + +func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil +} + +var parsedSchema = gqlparser.MustLoadSchema( + &ast.Source{Name: "schema.graphql", Input: `"The ` + "`" + `Upload` + "`" + ` scalar type represents a multipart file upload." +scalar Upload + +"The ` + "`" + `File` + "`" + ` type, represents the response of uploading a file." +type File { + id: Int! + name: String! + content: String! +} + +"The ` + "`" + `UploadFile` + "`" + ` type, represents the request for uploading a file with certain payload." +input UploadFile { + id: Int! + file: Upload! +} + +"The ` + "`" + `Query` + "`" + ` type, represents all of the entry points into our object graph." +type Query { + empty: String! +} + +"The ` + "`" + `Mutation` + "`" + ` type, represents all updates we can make to our data." +type Mutation { + singleUpload(file: Upload!): File! + singleUploadWithPayload(req: UploadFile!): File! + multipleUpload(files: [Upload!]!): [File!]! + multipleUploadWithPayload(req: [UploadFile!]!): [File!]! +} + +`}, +) + +// endregion ************************** generated!.gotpl ************************** + +// region ***************************** args.gotpl ***************************** + +func (ec *executionContext) field_Mutation_multipleUploadWithPayload_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 []model.UploadFile + if tmp, ok := rawArgs["req"]; ok { + arg0, err = ec.unmarshalNUploadFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx, tmp) + if err != nil { + return nil, err + } + } + args["req"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_multipleUpload_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 []graphql.Upload + if tmp, ok := rawArgs["files"]; ok { + arg0, err = ec.unmarshalNUpload2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, tmp) + if err != nil { + return nil, err + } + } + args["files"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_singleUploadWithPayload_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 model.UploadFile + if tmp, ok := rawArgs["req"]; ok { + arg0, err = ec.unmarshalNUploadFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx, tmp) + if err != nil { + return nil, err + } + } + args["req"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Mutation_singleUpload_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 graphql.Upload + if tmp, ok := rawArgs["file"]; ok { + arg0, err = ec.unmarshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, tmp) + if err != nil { + return nil, err + } + } + args["file"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 string + if tmp, ok := rawArgs["name"]; ok { + arg0, err = ec.unmarshalNString2string(ctx, tmp) + if err != nil { + return nil, err + } + } + args["name"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 bool + if tmp, ok := rawArgs["includeDeprecated"]; ok { + arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) + if err != nil { + return nil, err + } + } + args["includeDeprecated"] = arg0 + return args, nil +} + +// endregion ***************************** args.gotpl ***************************** + +// region **************************** field.gotpl ***************************** + +func (ec *executionContext) _File_id(ctx context.Context, field graphql.CollectedField, obj *model.File) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "File", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.ID, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(int) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNInt2int(ctx, field.Selections, res) +} + +func (ec *executionContext) _File_name(ctx context.Context, field graphql.CollectedField, obj *model.File) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "File", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _File_content(ctx context.Context, field graphql.CollectedField, obj *model.File) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "File", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Content, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_singleUpload(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_singleUpload_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx.Args = args + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().SingleUpload(rctx, args["file"].(graphql.Upload)) + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.File) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNFile2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_singleUploadWithPayload(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_singleUploadWithPayload_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx.Args = args + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().SingleUploadWithPayload(rctx, args["req"].(model.UploadFile)) + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.File) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNFile2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_multipleUpload(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_multipleUpload_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx.Args = args + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().MultipleUpload(rctx, args["files"].([]graphql.Upload)) + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]model.File) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, field.Selections, res) +} + +func (ec *executionContext) _Mutation_multipleUploadWithPayload(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "Mutation", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Mutation_multipleUploadWithPayload_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx.Args = args + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().MultipleUploadWithPayload(rctx, args["req"].([]model.UploadFile)) + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]model.File) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query_empty(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().Empty(rctx) + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field_Query___type_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx.Args = args + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectType(args["name"].(string)) + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) _Query___schema(ctx context.Context, field graphql.CollectedField) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "Query", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, nil, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.introspectSchema() + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Schema) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Directive", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Directive_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Directive", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Directive_locations(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Directive", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Locations, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__DirectiveLocation2ᚕstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Directive_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Directive", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Args, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, field.Selections, res) +} + +func (ec *executionContext) ___EnumValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__EnumValue", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___EnumValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__EnumValue", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___EnumValue_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__EnumValue", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) ___EnumValue_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.EnumValue) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__EnumValue", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_args(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Args, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_type(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_isDeprecated(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsDeprecated(), nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Field_deprecationReason(ctx context.Context, field graphql.CollectedField, obj *introspection.Field) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Field", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeprecationReason(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___InputValue_name(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__InputValue", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalNString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___InputValue_description(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__InputValue", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___InputValue_type(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__InputValue", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Type, nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___InputValue_defaultValue(ctx context.Context, field graphql.CollectedField, obj *introspection.InputValue) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__InputValue", + Field: field, + Args: nil, + IsMethod: false, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DefaultValue, nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_types(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Types(), nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_queryType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.QueryType(), nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_mutationType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.MutationType(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_subscriptionType(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SubscriptionType(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Schema_directives(ctx context.Context, field graphql.CollectedField, obj *introspection.Schema) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Schema", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Directives(), nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.([]introspection.Directive) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_kind(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Kind(), nil + }) + if resTmp == nil { + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalN__TypeKind2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_description(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Description(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(string) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalOString2string(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_fields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field___Type_fields_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx.Args = args + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Fields(args["includeDeprecated"].(bool)), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Field) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_interfaces(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Interfaces(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_possibleTypes(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PossibleTypes(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_enumValues(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + rawArgs := field.ArgumentMap(ec.Variables) + args, err := ec.field___Type_enumValues_args(ctx, rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + rctx.Args = args + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.EnumValues(args["includeDeprecated"].(bool)), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.EnumValue) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_inputFields(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.InputFields(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]introspection.InputValue) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, field.Selections, res) +} + +func (ec *executionContext) ___Type_ofType(ctx context.Context, field graphql.CollectedField, obj *introspection.Type) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func() { ec.Tracer.EndFieldExecution(ctx) }() + rctx := &graphql.ResolverContext{ + Object: "__Type", + Field: field, + Args: nil, + IsMethod: true, + } + ctx = graphql.WithResolverContext(ctx, rctx) + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, obj, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.OfType(), nil + }) + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*introspection.Type) + rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) + return ec.marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, field.Selections, res) +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +func (ec *executionContext) unmarshalInputUploadFile(ctx context.Context, v interface{}) (model.UploadFile, error) { + var it model.UploadFile + var asMap = v.(map[string]interface{}) + + for k, v := range asMap { + switch k { + case "id": + var err error + it.ID, err = ec.unmarshalNInt2int(ctx, v) + if err != nil { + return it, err + } + case "file": + var err error + it.File, err = ec.unmarshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, v) + if err != nil { + return it, err + } + } + } + + return it, nil +} + +// endregion **************************** input.gotpl ***************************** + +// region ************************** interface.gotpl *************************** + +// endregion ************************** interface.gotpl *************************** + +// region **************************** object.gotpl **************************** + +var fileImplementors = []string{"File"} + +func (ec *executionContext) _File(ctx context.Context, sel ast.SelectionSet, obj *model.File) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, fileImplementors) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("File") + case "id": + out.Values[i] = ec._File_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "name": + out.Values[i] = ec._File_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "content": + out.Values[i] = ec._File_content(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +var mutationImplementors = []string{"Mutation"} + +func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, mutationImplementors) + + ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ + Object: "Mutation", + }) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Mutation") + case "singleUpload": + out.Values[i] = ec._Mutation_singleUpload(ctx, field) + if out.Values[i] == graphql.Null { + invalid = true + } + case "singleUploadWithPayload": + out.Values[i] = ec._Mutation_singleUploadWithPayload(ctx, field) + if out.Values[i] == graphql.Null { + invalid = true + } + case "multipleUpload": + out.Values[i] = ec._Mutation_multipleUpload(ctx, field) + if out.Values[i] == graphql.Null { + invalid = true + } + case "multipleUploadWithPayload": + out.Values[i] = ec._Mutation_multipleUploadWithPayload(ctx, field) + if out.Values[i] == graphql.Null { + invalid = true + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +var queryImplementors = []string{"Query"} + +func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, queryImplementors) + + ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ + Object: "Query", + }) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("Query") + case "empty": + field := field + out.Concurrently(i, func() (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_empty(ctx, field) + if res == graphql.Null { + invalid = true + } + return res + }) + case "__type": + out.Values[i] = ec._Query___type(ctx, field) + case "__schema": + out.Values[i] = ec._Query___schema(ctx, field) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +var __DirectiveImplementors = []string{"__Directive"} + +func (ec *executionContext) ___Directive(ctx context.Context, sel ast.SelectionSet, obj *introspection.Directive) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, __DirectiveImplementors) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Directive") + case "name": + out.Values[i] = ec.___Directive_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "description": + out.Values[i] = ec.___Directive_description(ctx, field, obj) + case "locations": + out.Values[i] = ec.___Directive_locations(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "args": + out.Values[i] = ec.___Directive_args(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +var __EnumValueImplementors = []string{"__EnumValue"} + +func (ec *executionContext) ___EnumValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.EnumValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, __EnumValueImplementors) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__EnumValue") + case "name": + out.Values[i] = ec.___EnumValue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "description": + out.Values[i] = ec.___EnumValue_description(ctx, field, obj) + case "isDeprecated": + out.Values[i] = ec.___EnumValue_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "deprecationReason": + out.Values[i] = ec.___EnumValue_deprecationReason(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +var __FieldImplementors = []string{"__Field"} + +func (ec *executionContext) ___Field(ctx context.Context, sel ast.SelectionSet, obj *introspection.Field) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, __FieldImplementors) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Field") + case "name": + out.Values[i] = ec.___Field_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "description": + out.Values[i] = ec.___Field_description(ctx, field, obj) + case "args": + out.Values[i] = ec.___Field_args(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "type": + out.Values[i] = ec.___Field_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "isDeprecated": + out.Values[i] = ec.___Field_isDeprecated(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "deprecationReason": + out.Values[i] = ec.___Field_deprecationReason(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +var __InputValueImplementors = []string{"__InputValue"} + +func (ec *executionContext) ___InputValue(ctx context.Context, sel ast.SelectionSet, obj *introspection.InputValue) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, __InputValueImplementors) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__InputValue") + case "name": + out.Values[i] = ec.___InputValue_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "description": + out.Values[i] = ec.___InputValue_description(ctx, field, obj) + case "type": + out.Values[i] = ec.___InputValue_type(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "defaultValue": + out.Values[i] = ec.___InputValue_defaultValue(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +var __SchemaImplementors = []string{"__Schema"} + +func (ec *executionContext) ___Schema(ctx context.Context, sel ast.SelectionSet, obj *introspection.Schema) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, __SchemaImplementors) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Schema") + case "types": + out.Values[i] = ec.___Schema_types(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "queryType": + out.Values[i] = ec.___Schema_queryType(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "mutationType": + out.Values[i] = ec.___Schema_mutationType(ctx, field, obj) + case "subscriptionType": + out.Values[i] = ec.___Schema_subscriptionType(ctx, field, obj) + case "directives": + out.Values[i] = ec.___Schema_directives(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +var __TypeImplementors = []string{"__Type"} + +func (ec *executionContext) ___Type(ctx context.Context, sel ast.SelectionSet, obj *introspection.Type) graphql.Marshaler { + fields := graphql.CollectFields(ec.RequestContext, sel, __TypeImplementors) + + out := graphql.NewFieldSet(fields) + invalid := false + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("__Type") + case "kind": + out.Values[i] = ec.___Type_kind(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalid = true + } + case "name": + out.Values[i] = ec.___Type_name(ctx, field, obj) + case "description": + out.Values[i] = ec.___Type_description(ctx, field, obj) + case "fields": + out.Values[i] = ec.___Type_fields(ctx, field, obj) + case "interfaces": + out.Values[i] = ec.___Type_interfaces(ctx, field, obj) + case "possibleTypes": + out.Values[i] = ec.___Type_possibleTypes(ctx, field, obj) + case "enumValues": + out.Values[i] = ec.___Type_enumValues(ctx, field, obj) + case "inputFields": + out.Values[i] = ec.___Type_inputFields(ctx, field, obj) + case "ofType": + out.Values[i] = ec.___Type_ofType(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch() + if invalid { + return graphql.Null + } + return out +} + +// endregion **************************** object.gotpl **************************** + +// region ***************************** type.gotpl ***************************** + +func (ec *executionContext) unmarshalNBoolean2bool(ctx context.Context, v interface{}) (bool, error) { + return graphql.UnmarshalBoolean(v) +} + +func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + return graphql.MarshalBoolean(v) +} + +func (ec *executionContext) marshalNFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx context.Context, sel ast.SelectionSet, v model.File) graphql.Marshaler { + return ec._File(ctx, sel, &v) +} + +func (ec *executionContext) marshalNFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx context.Context, sel ast.SelectionSet, v []model.File) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalNFile2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐFile(ctx context.Context, sel ast.SelectionSet, v *model.File) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec._File(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNInt2int(ctx context.Context, v interface{}) (int, error) { + return graphql.UnmarshalInt(v) +} + +func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.SelectionSet, v int) graphql.Marshaler { + return graphql.MarshalInt(v) +} + +func (ec *executionContext) unmarshalNString2string(ctx context.Context, v interface{}) (string, error) { + return graphql.UnmarshalString(v) +} + +func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + return graphql.MarshalString(v) +} + +func (ec *executionContext) unmarshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, v interface{}) (graphql.Upload, error) { + return graphql.UnmarshalUpload(v) +} + +func (ec *executionContext) marshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, sel ast.SelectionSet, v graphql.Upload) graphql.Marshaler { + return graphql.MarshalUpload(v) +} + +func (ec *executionContext) unmarshalNUpload2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, v interface{}) ([]graphql.Upload, error) { + var vSlice []interface{} + if v != nil { + if tmp1, ok := v.([]interface{}); ok { + vSlice = tmp1 + } else { + vSlice = []interface{}{v} + } + } + var err error + res := make([]graphql.Upload, len(vSlice)) + for i := range vSlice { + res[i], err = ec.unmarshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalNUpload2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx context.Context, sel ast.SelectionSet, v []graphql.Upload) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + for i := range v { + ret[i] = ec.marshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, sel, v[i]) + } + + return ret +} + +func (ec *executionContext) unmarshalNUploadFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx context.Context, v interface{}) (model.UploadFile, error) { + return ec.unmarshalInputUploadFile(ctx, v) +} + +func (ec *executionContext) unmarshalNUploadFile2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx context.Context, v interface{}) ([]model.UploadFile, error) { + var vSlice []interface{} + if v != nil { + if tmp1, ok := v.([]interface{}); ok { + vSlice = tmp1 + } else { + vSlice = []interface{}{v} + } + } + var err error + res := make([]model.UploadFile, len(vSlice)) + for i := range vSlice { + res[i], err = ec.unmarshalNUploadFile2githubᚗcomᚋ99designsᚋgqlgenᚋexampleᚋfileuploadᚋmodelᚐUploadFile(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v introspection.Directive) graphql.Marshaler { + return ec.___Directive(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Directive2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx context.Context, sel ast.SelectionSet, v []introspection.Directive) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__Directive2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐDirective(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) unmarshalN__DirectiveLocation2string(ctx context.Context, v interface{}) (string, error) { + return graphql.UnmarshalString(v) +} + +func (ec *executionContext) marshalN__DirectiveLocation2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + return graphql.MarshalString(v) +} + +func (ec *executionContext) unmarshalN__DirectiveLocation2ᚕstring(ctx context.Context, v interface{}) ([]string, error) { + var vSlice []interface{} + if v != nil { + if tmp1, ok := v.([]interface{}); ok { + vSlice = tmp1 + } else { + vSlice = []interface{}{v} + } + } + var err error + res := make([]string, len(vSlice)) + for i := range vSlice { + res[i], err = ec.unmarshalN__DirectiveLocation2string(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalN__DirectiveLocation2ᚕstring(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__DirectiveLocation2string(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx context.Context, sel ast.SelectionSet, v introspection.EnumValue) graphql.Marshaler { + return ec.___EnumValue(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx context.Context, sel ast.SelectionSet, v introspection.Field) graphql.Marshaler { + return ec.___Field(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx context.Context, sel ast.SelectionSet, v introspection.InputValue) graphql.Marshaler { + return ec.___InputValue(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v introspection.Type) graphql.Marshaler { + return ec.___Type(ctx, sel, &v) +} + +func (ec *executionContext) marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + if v == nil { + if !ec.HasError(graphql.GetResolverContext(ctx)) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + return ec.___Type(ctx, sel, v) +} + +func (ec *executionContext) unmarshalN__TypeKind2string(ctx context.Context, v interface{}) (string, error) { + return graphql.UnmarshalString(v) +} + +func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + return graphql.MarshalString(v) +} + +func (ec *executionContext) unmarshalOBoolean2bool(ctx context.Context, v interface{}) (bool, error) { + return graphql.UnmarshalBoolean(v) +} + +func (ec *executionContext) marshalOBoolean2bool(ctx context.Context, sel ast.SelectionSet, v bool) graphql.Marshaler { + return graphql.MarshalBoolean(v) +} + +func (ec *executionContext) unmarshalOBoolean2ᚖbool(ctx context.Context, v interface{}) (*bool, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOBoolean2bool(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalOBoolean2ᚖbool(ctx context.Context, sel ast.SelectionSet, v *bool) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.marshalOBoolean2bool(ctx, sel, *v) +} + +func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) { + return graphql.UnmarshalString(v) +} + +func (ec *executionContext) marshalOString2string(ctx context.Context, sel ast.SelectionSet, v string) graphql.Marshaler { + return graphql.MarshalString(v) +} + +func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalOString2string(ctx, v) + return &res, err +} + +func (ec *executionContext) marshalOString2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.marshalOString2string(ctx, sel, *v) +} + +func (ec *executionContext) marshalO__EnumValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx context.Context, sel ast.SelectionSet, v []introspection.EnumValue) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__EnumValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐEnumValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalO__Field2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx context.Context, sel ast.SelectionSet, v []introspection.Field) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__Field2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐField(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalO__InputValue2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx context.Context, sel ast.SelectionSet, v []introspection.InputValue) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__InputValue2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐInputValue(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalO__Schema2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx context.Context, sel ast.SelectionSet, v introspection.Schema) graphql.Marshaler { + return ec.___Schema(ctx, sel, &v) +} + +func (ec *executionContext) marshalO__Schema2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐSchema(ctx context.Context, sel ast.SelectionSet, v *introspection.Schema) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.___Schema(ctx, sel, v) +} + +func (ec *executionContext) marshalO__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v introspection.Type) graphql.Marshaler { + return ec.___Type(ctx, sel, &v) +} + +func (ec *executionContext) marshalO__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v []introspection.Type) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + rctx := &graphql.ResolverContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalN__Type2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + +func (ec *executionContext) marshalO__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec.___Type(ctx, sel, v) +} + +// endregion ***************************** type.gotpl ***************************** diff --git a/example/fileupload/model/generated.go b/example/fileupload/model/generated.go new file mode 100644 index 0000000000..c311e9adfe --- /dev/null +++ b/example/fileupload/model/generated.go @@ -0,0 +1,20 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package model + +import ( + "github.com/99designs/gqlgen/graphql" +) + +// 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"` +} + +// The `UploadFile` type, represents the request for uploading a file with certain payload. +type UploadFile struct { + ID int `json:"id"` + File graphql.Upload `json:"file"` +} diff --git a/example/fileupload/readme.md b/example/fileupload/readme.md new file mode 100644 index 0000000000..ae0eb4d095 --- /dev/null +++ b/example/fileupload/readme.md @@ -0,0 +1,229 @@ +### fileupload example + +This server demonstrates how to handle file upload + +to run this server +```bash +go run ./example/fileupload/server/server.go +``` + +and open http://localhost:8087 in your browser + + +### Single file + +#### Operations + +``` +{ + query: ` + mutation($file: Upload!) { + singleUpload(file: $file) { + id + } + } + `, + variables: { + file: File // a.txt + } +} +``` + +#### cURL request + +```shell +curl localhost:8087/query \ + -F operations='{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id, name, content } }", "variables": { "file": null } }' \ + -F map='{ "0": ["variables.file"] }' \ + -F 0=@./example/fileupload./testfiles/a.txt +``` + +#### Request payload + +``` +--------------------------e6b2b29561e71173 +Content-Disposition: form-data; name="operations" + +{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id, name, content } }", "variables": { "file": null } } +--------------------------e6b2b29561e71173 +Content-Disposition: form-data; name="map" + +{ "0": ["variables.file"] } +--------------------------e6b2b29561e71173 +Content-Disposition: form-data; name="0"; filename="a.txt" +Content-Type: text/plain + +Alpha file content. +--------------------------e6b2b29561e71173-- +``` + +### Single file with payload + +#### Operations + +``` +{ + query: ` + mutation($file: Upload!) { + singleUpload(file: $file) { + id + } + } + `, + variables: { + file: File // a.txt + } +} +``` + +#### cURL request + +```shell +curl localhost:8087/query \ + -F operations='{ "query": "mutation ($req: UploadFile!) { singleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": {"file": null, "id": 1 } } }' \ + -F map='{ "0": ["variables.req.file"] }' \ + -F 0=@./example/fileupload/testfiles/a.txt +``` + +#### Request payload + +``` +--------------------------38752760889d14aa +Content-Disposition: form-data; name="operations" + +{ "query": "mutation ($req: UploadFile!) { singleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": {"file": null, "id": 1 } } } +--------------------------38752760889d14aa +Content-Disposition: form-data; name="map" + +{ "0": ["variables.req.file"] } +--------------------------38752760889d14aa +Content-Disposition: form-data; name="0"; filename="a.txt" +Content-Type: text/plain + +Alpha file content. +--------------------------38752760889d14aa-- +``` + + +### File list + +#### Operations + +``` +{ + query: ` + mutation($files: [Upload!]!) { + multipleUpload(files: $files) { + id + } + } + `, + variables: { + files: [ + File, // b.txt + File // c.txt + ] + } +} +``` + +#### cURL request + +``` +curl localhost:8087/query \ + -F operations='{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id, name, content } }", "variables": { "files": [null, null] } }' \ + -F map='{ "0": ["variables.files.0"], "1": ["variables.files.1"] }' \ + -F 0=@./example/fileupload/testfiles/b.txt \ + -F 1=@./example/fileupload/testfiles/c.txt +``` + +#### Request payload + +``` +--------------------------d7aca2a93c3655e0 +Content-Disposition: form-data; name="operations" + +{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id, name, content } }", "variables": { "files": [null, null] } } +--------------------------d7aca2a93c3655e0 +Content-Disposition: form-data; name="map" + +{ "0": ["variables.files.0"], "1": ["variables.files.1"] } +--------------------------d7aca2a93c3655e0 +Content-Disposition: form-data; name="0"; filename="b.txt" +Content-Type: text/plain + +Bravo file content. +--------------------------d7aca2a93c3655e0 +Content-Disposition: form-data; name="1"; filename="c.txt" +Content-Type: text/plain + +Charlie file content. +--------------------------d7aca2a93c3655e0-- +``` + + + +### File list with payload + +#### Operations + +``` +{ + query: ` + mutation($req: [UploadFile!]!) + multipleUploadWithPayload(req: $req) { + id, + name, + content + } + } + `, + variables: { + req: [ + { + id: 1, + File, // b.txt + }, + { + id: 2, + File, // c.txt + } + ] + } +} +``` + +#### cURL request + +``` +curl localhost:8087/query \ + -F operations='{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }' \ + -F map='{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] }' \ + -F 0=@./example/fileupload/testfiles/b.txt \ + -F 1=@./example/fileupload/testfiles/c.txt +``` + +#### Request payload + +``` +--------------------------65aab09fb49ee66f +Content-Disposition: form-data; name="operations" + +{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } } +--------------------------65aab09fb49ee66f +Content-Disposition: form-data; name="map" + +{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] } +--------------------------65aab09fb49ee66f +Content-Disposition: form-data; name="0"; filename="b.txt" +Content-Type: text/plain + +Bravo file content. +--------------------------65aab09fb49ee66f +Content-Disposition: form-data; name="1"; filename="c.txt" +Content-Type: text/plain + +Charlie file content. +--------------------------65aab09fb49ee66f-- +``` + diff --git a/example/fileupload/resolvers.go b/example/fileupload/resolvers.go new file mode 100644 index 0000000000..ad11c38352 --- /dev/null +++ b/example/fileupload/resolvers.go @@ -0,0 +1,59 @@ +//go:generate go run ../../testdata/gqlgen.go + +package fileupload + +import ( + "context" + "fmt" + + "github.com/99designs/gqlgen/example/fileupload/model" + "github.com/99designs/gqlgen/graphql" +) + +type Resolver struct { + SingleUploadFunc func(ctx context.Context, file graphql.Upload) (*model.File, error) + SingleUploadWithPayloadFunc func(ctx context.Context, req model.UploadFile) (*model.File, error) + MultipleUploadFunc func(ctx context.Context, files []graphql.Upload) ([]model.File, error) + MultipleUploadWithPayloadFunc func(ctx context.Context, req []model.UploadFile) ([]model.File, error) +} + +func (r *Resolver) Mutation() MutationResolver { + return &mutationResolver{r} +} +func (r *Resolver) Query() QueryResolver { + return &queryResolver{r} +} + +type mutationResolver struct{ *Resolver } + +func (r *mutationResolver) SingleUpload(ctx context.Context, file graphql.Upload) (*model.File, error) { + if r.SingleUploadFunc != nil { + return r.SingleUploadFunc(ctx, file) + } + return nil, fmt.Errorf("not implemented") +} +func (r *mutationResolver) SingleUploadWithPayload(ctx context.Context, req model.UploadFile) (*model.File, error) { + if r.SingleUploadWithPayloadFunc != nil { + return r.SingleUploadWithPayloadFunc(ctx, req) + } + return nil, fmt.Errorf("not implemented") +} +func (r *mutationResolver) MultipleUpload(ctx context.Context, files []graphql.Upload) ([]model.File, error) { + if r.MultipleUploadFunc != nil { + return r.MultipleUploadFunc(ctx, files) + } + return nil, fmt.Errorf("not implemented") +} + +func (r *mutationResolver) MultipleUploadWithPayload(ctx context.Context, req []model.UploadFile) ([]model.File, error) { + if r.MultipleUploadWithPayloadFunc != nil { + return r.MultipleUploadWithPayloadFunc(ctx, req) + } + return nil, fmt.Errorf("not implemented") +} + +type queryResolver struct{ *Resolver } + +func (r *queryResolver) Empty(ctx context.Context) (string, error) { + return "", fmt.Errorf("not implemented") +} diff --git a/example/fileupload/schema.graphql b/example/fileupload/schema.graphql new file mode 100644 index 0000000000..891c61ec93 --- /dev/null +++ b/example/fileupload/schema.graphql @@ -0,0 +1,29 @@ +"The `Upload` scalar type represents a multipart file upload." +scalar Upload + +"The `File` type, represents the response of uploading a file." +type File { + id: Int! + name: String! + content: String! +} + +"The `UploadFile` type, represents the request for uploading a file with certain payload." +input UploadFile { + id: Int! + file: Upload! +} + +"The `Query` type, represents all of the entry points into our object graph." +type Query { + empty: String! +} + +"The `Mutation` type, represents all updates we can make to our data." +type Mutation { + singleUpload(file: Upload!): File! + singleUploadWithPayload(req: UploadFile!): File! + multipleUpload(files: [Upload!]!): [File!]! + multipleUploadWithPayload(req: [UploadFile!]!): [File!]! +} + diff --git a/example/fileupload/server/server.go b/example/fileupload/server/server.go new file mode 100644 index 0000000000..fdc72aab81 --- /dev/null +++ b/example/fileupload/server/server.go @@ -0,0 +1,92 @@ +package main + +import ( + "context" + "errors" + "io/ioutil" + "log" + "net/http" + + "github.com/99designs/gqlgen/example/fileupload" + "github.com/99designs/gqlgen/example/fileupload/model" + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/handler" +) + +func main() { + http.Handle("/", handler.Playground("File Upload Demo", "/query")) + resolver := getResolver() + exec := fileupload.NewExecutableSchema(fileupload.Config{Resolvers: resolver}) + + var mb int64 = 1 << 20 + uploadMaxMemory := handler.UploadMaxMemory(32 * mb) + uploadMaxSize := handler.UploadMaxSize(50 * mb) + + http.Handle("/query", handler.GraphQL(exec, uploadMaxMemory, uploadMaxSize)) + log.Print("connect to http://localhost:8087/ for GraphQL playground") + log.Fatal(http.ListenAndServe(":8087", nil)) +} + +func getResolver() *fileupload.Resolver { + resolver := &fileupload.Resolver{ + SingleUploadFunc: func(ctx context.Context, file graphql.Upload) (*model.File, error) { + content, err := ioutil.ReadAll(file.File) + if err != nil { + return nil, err + } + return &model.File{ + ID: 1, + Name: file.Filename, + Content: string(content), + }, nil + }, + SingleUploadWithPayloadFunc: func(ctx context.Context, req model.UploadFile) (*model.File, error) { + content, err := ioutil.ReadAll(req.File.File) + if err != nil { + return nil, err + } + return &model.File{ + ID: 1, + Name: req.File.Filename, + Content: string(content), + }, nil + }, + MultipleUploadFunc: func(ctx context.Context, files []graphql.Upload) ([]model.File, error) { + if len(files) == 0 { + return nil, errors.New("empty list") + } + var resp []model.File + for i := range files { + content, err := ioutil.ReadAll(files[i].File) + if err != nil { + return []model.File{}, err + } + resp = append(resp, model.File{ + ID: i + 1, + Name: files[i].Filename, + Content: string(content), + }) + } + return resp, nil + }, + MultipleUploadWithPayloadFunc: func(ctx context.Context, req []model.UploadFile) ([]model.File, error) { + if len(req) == 0 { + return nil, errors.New("empty list") + } + var resp []model.File + for i := range req { + content, err := ioutil.ReadAll(req[i].File.File) + if err != nil { + return []model.File{}, err + } + resp = append(resp, model.File{ + ID: i + 1, + Name: req[i].File.Filename, + Content: string(content), + }) + } + return resp, nil + }, + } + return resolver +} diff --git a/example/fileupload/testfiles/a.txt b/example/fileupload/testfiles/a.txt new file mode 100644 index 0000000000..96ff01944e --- /dev/null +++ b/example/fileupload/testfiles/a.txt @@ -0,0 +1 @@ +Alpha file content diff --git a/example/fileupload/testfiles/b.txt b/example/fileupload/testfiles/b.txt new file mode 100644 index 0000000000..32b94fd894 --- /dev/null +++ b/example/fileupload/testfiles/b.txt @@ -0,0 +1 @@ +Bravo file content diff --git a/example/fileupload/testfiles/c.txt b/example/fileupload/testfiles/c.txt new file mode 100644 index 0000000000..cfec6a422f --- /dev/null +++ b/example/fileupload/testfiles/c.txt @@ -0,0 +1 @@ +Charlie file content diff --git a/graphql/upload.go b/graphql/upload.go new file mode 100644 index 0000000000..22d6103149 --- /dev/null +++ b/graphql/upload.go @@ -0,0 +1,26 @@ +package graphql + +import ( + "fmt" + "io" +) + +type Upload struct { + File io.Reader + Filename string + Size int64 +} + +func MarshalUpload(f Upload) Marshaler { + return WriterFunc(func(w io.Writer) { + io.Copy(w, f.File) + }) +} + +func UnmarshalUpload(v interface{}) (Upload, error) { + upload, ok := v.(Upload) + if !ok { + return Upload{}, fmt.Errorf("%T is not an Upload", v) + } + return upload, nil +} diff --git a/handler/graphql.go b/handler/graphql.go index 92a0471ce2..6d2a787a03 100644 --- a/handler/graphql.go +++ b/handler/graphql.go @@ -3,9 +3,13 @@ package handler import ( "context" "encoding/json" + "errors" "fmt" "io" + "io/ioutil" "net/http" + "os" + "strconv" "strings" "time" @@ -37,6 +41,8 @@ type Config struct { complexityLimitFunc graphql.ComplexityLimitFunc disableIntrospection bool connectionKeepAlivePingInterval time.Duration + uploadMaxMemory int64 + uploadMaxSize int64 } func (c *Config) newRequestContext(es graphql.ExecutableSchema, doc *ast.QueryDocument, op *ast.OperationDefinition, query string, variables map[string]interface{}) *graphql.RequestContext { @@ -251,6 +257,23 @@ func CacheSize(size int) Option { } } +// UploadMaxSize sets the maximum number of bytes used to parse a request body +// as multipart/form-data. +func UploadMaxSize(size int64) Option { + return func(cfg *Config) { + cfg.uploadMaxSize = size + } +} + +// UploadMaxMemory sets the maximum number of bytes used to parse a request body +// as multipart/form-data in memory, with the remainder stored on disk in +// temporary files. +func UploadMaxMemory(size int64) Option { + return func(cfg *Config) { + cfg.uploadMaxMemory = size + } +} + // WebsocketKeepAliveDuration allows you to reconfigure the keepalive behavior. // By default, keepalive is enabled with a DefaultConnectionKeepAlivePingInterval // duration. Set handler.connectionKeepAlivePingInterval = 0 to disable keepalive @@ -264,9 +287,20 @@ func WebsocketKeepAliveDuration(duration time.Duration) Option { const DefaultCacheSize = 1000 const DefaultConnectionKeepAlivePingInterval = 25 * time.Second +// DefaultUploadMaxMemory is the maximum number of bytes used to parse a request body +// as multipart/form-data in memory, with the remainder stored on disk in +// temporary files. +const DefaultUploadMaxMemory = 32 << 20 + +// DefaultUploadMaxSize is maximum number of bytes used to parse a request body +// as multipart/form-data. +const DefaultUploadMaxSize = 32 << 20 + func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc { cfg := &Config{ cacheSize: DefaultCacheSize, + uploadMaxMemory: DefaultUploadMaxMemory, + uploadMaxSize: DefaultUploadMaxSize, connectionKeepAlivePingInterval: DefaultConnectionKeepAlivePingInterval, upgrader: websocket.Upgrader{ ReadBufferSize: 1024, @@ -335,9 +369,27 @@ func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } case http.MethodPost: - if err := jsonDecode(r.Body, &reqParams); err != nil { - sendErrorf(w, http.StatusBadRequest, "json body could not be decoded: "+err.Error()) - return + contentType := strings.SplitN(r.Header.Get("Content-Type"), ";", 2)[0] + if contentType == "multipart/form-data" { + var closers []io.Closer + var tmpFiles []string + defer func() { + for i := len(closers) - 1; 0 <= i; i-- { + _ = closers[i].Close() + } + for _, tmpFile := range tmpFiles { + _ = os.Remove(tmpFile) + } + }() + if err := processMultipart(w, r, &reqParams, &closers, &tmpFiles, gh.cfg.uploadMaxSize, gh.cfg.uploadMaxMemory); err != nil { + sendErrorf(w, http.StatusBadRequest, "multipart body could not be decoded: "+err.Error()) + return + } + } else { + if err := jsonDecode(r.Body, &reqParams); err != nil { + sendErrorf(w, http.StatusBadRequest, "json body could not be decoded: "+err.Error()) + return + } } default: w.WriteHeader(http.StatusMethodNotAllowed) @@ -493,3 +545,154 @@ func sendError(w http.ResponseWriter, code int, errors ...*gqlerror.Error) { func sendErrorf(w http.ResponseWriter, code int, format string, args ...interface{}) { sendError(w, code, &gqlerror.Error{Message: fmt.Sprintf(format, args...)}) } + +type bytesReader struct { + s *[]byte + i int64 // current reading index + prevRune int // index of previous rune; or < 0 +} + +func (r *bytesReader) Read(b []byte) (n int, err error) { + if r.s == nil { + return 0, errors.New("byte slice pointer is nil") + } + if r.i >= int64(len(*r.s)) { + return 0, io.EOF + } + r.prevRune = -1 + n = copy(b, (*r.s)[r.i:]) + r.i += int64(n) + return +} + +func processMultipart(w http.ResponseWriter, r *http.Request, request *params, closers *[]io.Closer, tmpFiles *[]string, uploadMaxSize, uploadMaxMemory int64) error { + var err error + if r.ContentLength > uploadMaxSize { + return errors.New("failed to parse multipart form, request body too large") + } + r.Body = http.MaxBytesReader(w, r.Body, uploadMaxSize) + if err = r.ParseMultipartForm(uploadMaxMemory); err != nil { + if strings.Contains(err.Error(), "request body too large") { + return errors.New("failed to parse multipart form, request body too large") + } + return errors.New("failed to parse multipart form") + } + *closers = append(*closers, r.Body) + + if err = jsonDecode(strings.NewReader(r.Form.Get("operations")), &request); err != nil { + return errors.New("operations form field could not be decoded") + } + + var uploadsMap = map[string][]string{} + if err = json.Unmarshal([]byte(r.Form.Get("map")), &uploadsMap); err != nil { + return errors.New("map form field could not be decoded") + } + + var upload graphql.Upload + for key, paths := range uploadsMap { + if len(paths) == 0 { + return fmt.Errorf("invalid empty operations paths list for key %s", key) + } + file, header, err := r.FormFile(key) + if err != nil { + return fmt.Errorf("failed to get key %s from form", key) + } + *closers = append(*closers, file) + + if len(paths) == 1 { + upload = graphql.Upload{ + File: file, + Size: header.Size, + Filename: header.Filename, + } + err = addUploadToOperations(request, upload, key, paths[0]) + if err != nil { + return err + } + } else { + if r.ContentLength < uploadMaxMemory { + fileBytes, err := ioutil.ReadAll(file) + if err != nil { + return fmt.Errorf("failed to read file for key %s", key) + } + for _, path := range paths { + upload = graphql.Upload{ + File: &bytesReader{s: &fileBytes, i: 0, prevRune: -1}, + Size: header.Size, + Filename: header.Filename, + } + err = addUploadToOperations(request, upload, key, path) + if err != nil { + return err + } + } + } else { + tmpFile, err := ioutil.TempFile(os.TempDir(), "gqlgen-") + if err != nil { + return fmt.Errorf("failed to create temp file for key %s", key) + } + tmpName := tmpFile.Name() + *tmpFiles = append(*tmpFiles, tmpName) + _, err = io.Copy(tmpFile, file) + if err != nil { + if err := tmpFile.Close(); err != nil { + return fmt.Errorf("failed to copy to temp file and close temp file for key %s", key) + } + return fmt.Errorf("failed to copy to temp file for key %s", key) + } + if err := tmpFile.Close(); err != nil { + return fmt.Errorf("failed to close temp file for key %s", key) + } + for _, path := range paths { + pathTmpFile, err := os.Open(tmpName) + if err != nil { + return fmt.Errorf("failed to open temp file for key %s", key) + } + *closers = append(*closers, pathTmpFile) + upload = graphql.Upload{ + File: pathTmpFile, + Size: header.Size, + Filename: header.Filename, + } + err = addUploadToOperations(request, upload, key, path) + if err != nil { + return err + } + } + } + } + } + return nil +} + +func addUploadToOperations(request *params, upload graphql.Upload, key, path string) error { + if !strings.HasPrefix(path, "variables.") { + return fmt.Errorf("invalid operations paths for key %s", key) + } + + var ptr interface{} = request.Variables + parts := strings.Split(path, ".") + + // skip the first part (variables) because we started there + for i, p := range parts[1:] { + last := i == len(parts)-2 + if ptr == nil { + return fmt.Errorf("path is missing \"variables.\" prefix, key: %s, path: %s", key, path) + } + if index, parseNbrErr := strconv.Atoi(p); parseNbrErr == nil { + if last { + ptr.([]interface{})[index] = upload + } else { + ptr = ptr.([]interface{})[index] + } + } else { + if last { + ptr.(map[string]interface{})[p] = upload + } else { + ptr = ptr.(map[string]interface{})[p] + } + } + } + + return nil +} diff --git a/handler/graphql_test.go b/handler/graphql_test.go index f938640716..70946ab949 100644 --- a/handler/graphql_test.go +++ b/handler/graphql_test.go @@ -1,15 +1,24 @@ package handler import ( + "bytes" "context" + "fmt" + "io" + "io/ioutil" + "mime/multipart" "net/http" "net/http/httptest" + "os" "strings" + "sync" "testing" "github.com/99designs/gqlgen/graphql" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/vektah/gqlparser/ast" ) func TestHandlerPOST(t *testing.T) { @@ -176,6 +185,459 @@ func TestHandlerComplexity(t *testing.T) { }) } +func TestFileUpload(t *testing.T) { + + t.Run("valid single file upload", func(t *testing.T) { + mock := &executableSchemaMock{ + MutationFunc: func(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + require.Equal(t, len(op.VariableDefinitions), 1) + require.Equal(t, op.VariableDefinitions[0].Variable, "file") + return &graphql.Response{Data: []byte(`{"singleUpload":{"id":1}}`)} + }, + } + handler := GraphQL(mock) + + operations := `{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }` + mapData := `{ "0": ["variables.file"] }` + files := []file{ + { + mapKey: "0", + name: "a.txt", + content: "test1", + }, + } + req := createUploadRequest(t, operations, mapData, files) + + resp := httptest.NewRecorder() + handler.ServeHTTP(resp, req) + require.Equal(t, http.StatusOK, resp.Code) + require.Equal(t, `{"data":{"singleUpload":{"id":1}}}`, resp.Body.String()) + }) + + t.Run("valid single file upload with payload", func(t *testing.T) { + mock := &executableSchemaMock{ + MutationFunc: func(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + require.Equal(t, len(op.VariableDefinitions), 1) + require.Equal(t, op.VariableDefinitions[0].Variable, "req") + return &graphql.Response{Data: []byte(`{"singleUploadWithPayload":{"id":1}}`)} + }, + } + handler := GraphQL(mock) + + operations := `{ "query": "mutation ($req: UploadFile!) { singleUploadWithPayload(req: $req) { id } }", "variables": { "req": {"file": null, "id": 1 } } }` + mapData := `{ "0": ["variables.req.file"] }` + files := []file{ + { + mapKey: "0", + name: "a.txt", + content: "test1", + }, + } + req := createUploadRequest(t, operations, mapData, files) + + resp := httptest.NewRecorder() + handler.ServeHTTP(resp, req) + require.Equal(t, http.StatusOK, resp.Code) + require.Equal(t, `{"data":{"singleUploadWithPayload":{"id":1}}}`, resp.Body.String()) + }) + + t.Run("valid file list upload", func(t *testing.T) { + mock := &executableSchemaMock{ + MutationFunc: func(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + require.Equal(t, len(op.VariableDefinitions), 1) + require.Equal(t, op.VariableDefinitions[0].Variable, "files") + return &graphql.Response{Data: []byte(`{"multipleUpload":[{"id":1},{"id":2}]}`)} + }, + } + handler := GraphQL(mock) + + operations := `{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id } }", "variables": { "files": [null, null] } }` + mapData := `{ "0": ["variables.files.0"], "1": ["variables.files.1"] }` + files := []file{ + { + mapKey: "0", + name: "a.txt", + content: "test1", + }, + { + mapKey: "1", + name: "b.txt", + content: "test2", + }, + } + req := createUploadRequest(t, operations, mapData, files) + + resp := httptest.NewRecorder() + handler.ServeHTTP(resp, req) + require.Equal(t, http.StatusOK, resp.Code) + require.Equal(t, `{"data":{"multipleUpload":[{"id":1},{"id":2}]}}`, resp.Body.String()) + }) + + t.Run("valid file list upload with payload", func(t *testing.T) { + mock := &executableSchemaMock{ + MutationFunc: func(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + require.Equal(t, len(op.VariableDefinitions), 1) + require.Equal(t, op.VariableDefinitions[0].Variable, "req") + return &graphql.Response{Data: []byte(`{"multipleUploadWithPayload":[{"id":1},{"id":2}]}`)} + }, + } + handler := GraphQL(mock) + + operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id } }", "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: "1", + name: "b.txt", + content: "test2", + }, + } + req := createUploadRequest(t, operations, mapData, files) + + resp := httptest.NewRecorder() + handler.ServeHTTP(resp, req) + require.Equal(t, http.StatusOK, resp.Code) + require.Equal(t, `{"data":{"multipleUploadWithPayload":[{"id":1},{"id":2}]}}`, resp.Body.String()) + }) + + t.Run("valid file list upload with payload and file reuse", func(t *testing.T) { + test := func(uploadMaxMemory int64) { + mock := &executableSchemaMock{ + MutationFunc: func(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + require.Equal(t, len(op.VariableDefinitions), 1) + require.Equal(t, op.VariableDefinitions[0].Variable, "req") + return &graphql.Response{Data: []byte(`{"multipleUploadWithPayload":[{"id":1},{"id":2}]}`)} + }, + } + maxMemory := UploadMaxMemory(5000) + handler := GraphQL(mock, maxMemory) + + operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id } }", "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", + }, + } + req := createUploadRequest(t, operations, mapData, files) + + resp := httptest.NewRecorder() + handler.ServeHTTP(resp, req) + require.Equal(t, http.StatusOK, resp.Code) + require.Equal(t, `{"data":{"multipleUploadWithPayload":[{"id":1},{"id":2}]}}`, resp.Body.String()) + } + + t.Run("payload smaller than UploadMaxMemory, stored in memory", func(t *testing.T) { + test(5000) + }) + + t.Run("payload bigger than UploadMaxMemory, persisted to disk", func(t *testing.T) { + test(2) + }) + }) +} + +func TestProcessMultipart(t *testing.T) { + validOperations := `{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }` + validMap := `{ "0": ["variables.file"] }` + validFiles := []file{ + { + mapKey: "0", + name: "a.txt", + content: "test1", + }, + } + + cleanUp := func(t *testing.T, closers []io.Closer, tmpFiles []string) { + for i := len(closers) - 1; 0 <= i; i-- { + err := closers[i].Close() + require.Nil(t, err) + } + for _, tmpFiles := range tmpFiles { + err := os.Remove(tmpFiles) + require.Nil(t, err) + } + } + + t.Run("fail to parse multipart", func(t *testing.T) { + req := &http.Request{ + Method: "POST", + Header: http.Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, + Body: ioutil.NopCloser(new(bytes.Buffer)), + } + var reqParams params + var closers []io.Closer + var tmpFiles []string + w := httptest.NewRecorder() + err := processMultipart(w, req, &reqParams, &closers, &tmpFiles, DefaultUploadMaxSize, DefaultUploadMaxMemory) + require.NotNil(t, err) + require.Equal(t, err.Error(), "failed to parse multipart form") + cleanUp(t, closers, tmpFiles) + }) + + t.Run("fail parse operation", func(t *testing.T) { + operations := `invalid operation` + req := createUploadRequest(t, operations, validMap, validFiles) + + var reqParams params + var closers []io.Closer + var tmpFiles []string + w := httptest.NewRecorder() + err := processMultipart(w, req, &reqParams, &closers, &tmpFiles, DefaultUploadMaxSize, DefaultUploadMaxMemory) + require.NotNil(t, err) + require.Equal(t, err.Error(), "operations form field could not be decoded") + cleanUp(t, closers, tmpFiles) + }) + + t.Run("fail parse map", func(t *testing.T) { + mapData := `invalid map` + req := createUploadRequest(t, validOperations, mapData, validFiles) + + var reqParams params + var closers []io.Closer + var tmpFiles []string + w := httptest.NewRecorder() + err := processMultipart(w, req, &reqParams, &closers, &tmpFiles, DefaultUploadMaxSize, DefaultUploadMaxMemory) + require.NotNil(t, err) + require.Equal(t, err.Error(), "map form field could not be decoded") + cleanUp(t, closers, tmpFiles) + }) + + t.Run("fail missing file", func(t *testing.T) { + var files []file + req := createUploadRequest(t, validOperations, validMap, files) + + var reqParams params + var closers []io.Closer + var tmpFiles []string + w := httptest.NewRecorder() + err := processMultipart(w, req, &reqParams, &closers, &tmpFiles, DefaultUploadMaxSize, DefaultUploadMaxMemory) + require.NotNil(t, err) + require.Equal(t, err.Error(), "failed to get key 0 from form") + cleanUp(t, closers, tmpFiles) + }) + + t.Run("fail map entry with invalid operations paths prefix", func(t *testing.T) { + mapData := `{ "0": ["var.file"] }` + req := createUploadRequest(t, validOperations, mapData, validFiles) + + var reqParams params + var closers []io.Closer + var tmpFiles []string + w := httptest.NewRecorder() + err := processMultipart(w, req, &reqParams, &closers, &tmpFiles, DefaultUploadMaxSize, DefaultUploadMaxMemory) + require.NotNil(t, err) + require.Equal(t, err.Error(), "invalid operations paths for key 0") + cleanUp(t, closers, tmpFiles) + }) + + t.Run("fail parse request big body", func(t *testing.T) { + req := createUploadRequest(t, validOperations, validMap, validFiles) + + var reqParams params + var closers []io.Closer + var tmpFiles []string + w := httptest.NewRecorder() + var smallMaxSize int64 = 2 + err := processMultipart(w, req, &reqParams, &closers, &tmpFiles, smallMaxSize, DefaultUploadMaxMemory) + require.NotNil(t, err) + require.Equal(t, err.Error(), "failed to parse multipart form, request body too large") + cleanUp(t, closers, tmpFiles) + }) + + t.Run("valid request", func(t *testing.T) { + req := createUploadRequest(t, validOperations, validMap, validFiles) + + var reqParams params + var closers []io.Closer + var tmpFiles []string + w := httptest.NewRecorder() + err := processMultipart(w, req, &reqParams, &closers, &tmpFiles, DefaultUploadMaxSize, DefaultUploadMaxMemory) + require.Nil(t, err) + require.Equal(t, "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", reqParams.Query) + require.Equal(t, "", reqParams.OperationName) + require.Equal(t, 1, len(reqParams.Variables)) + require.NotNil(t, reqParams.Variables["file"]) + reqParamsFile, ok := reqParams.Variables["file"].(graphql.Upload) + require.True(t, ok) + require.Equal(t, "a.txt", reqParamsFile.Filename) + require.Equal(t, int64(len("test1")), reqParamsFile.Size) + content, err := ioutil.ReadAll(reqParamsFile.File) + require.Nil(t, err) + require.Equal(t, "test1", string(content)) + cleanUp(t, closers, tmpFiles) + }) + + t.Run("valid file list upload with payload and file reuse", func(t *testing.T) { + operations := `{ "query": "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id } }", "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", + }, + } + req := createUploadRequest(t, operations, mapData, files) + + test := func(uploadMaxMemory int64) { + var reqParams params + var closers []io.Closer + var tmpFiles []string + w := httptest.NewRecorder() + err := processMultipart(w, req, &reqParams, &closers, &tmpFiles, DefaultUploadMaxSize, uploadMaxMemory) + require.Nil(t, err) + require.Equal(t, "mutation($req: [UploadFile!]!) { multipleUploadWithPayload(req: $req) { id } }", reqParams.Query) + require.Equal(t, "", reqParams.OperationName) + require.Equal(t, 1, len(reqParams.Variables)) + require.NotNil(t, reqParams.Variables["req"]) + reqParamsFile, ok := reqParams.Variables["req"].([]interface{}) + require.True(t, ok) + require.Equal(t, 2, len(reqParamsFile)) + for i, item := range reqParamsFile { + itemMap := item.(map[string]interface{}) + require.Equal(t, fmt.Sprint(itemMap["id"]), fmt.Sprint(i+1)) + file := itemMap["file"].(graphql.Upload) + require.Equal(t, "a.txt", file.Filename) + require.Equal(t, int64(len("test1")), file.Size) + require.Nil(t, err) + content, err := ioutil.ReadAll(file.File) + require.Nil(t, err) + require.Equal(t, "test1", string(content)) + } + cleanUp(t, closers, tmpFiles) + } + + t.Run("payload smaller than UploadMaxMemory, stored in memory", func(t *testing.T) { + test(5000) + }) + + t.Run("payload bigger than UploadMaxMemory, persisted to disk", func(t *testing.T) { + test(2) + }) + }) +} + +func TestAddUploadToOperations(t *testing.T) { + key := "0" + + t.Run("fail missing all variables", func(t *testing.T) { + file, _ := os.Open("path/to/file") + request := ¶ms{} + + upload := graphql.Upload{ + File: file, + Filename: "a.txt", + Size: int64(5), + } + path := "variables.req.0.file" + err := addUploadToOperations(request, upload, key, path) + require.NotNil(t, err) + require.Equal(t, "path is missing \"variables.\" prefix, key: 0, path: variables.req.0.file", err.Error()) + }) + + t.Run("valid variable", func(t *testing.T) { + file, _ := os.Open("path/to/file") + request := ¶ms{ + Variables: map[string]interface{}{ + "file": nil, + }, + } + + upload := graphql.Upload{ + File: file, + Filename: "a.txt", + Size: int64(5), + } + + expected := ¶ms{ + Variables: map[string]interface{}{ + "file": upload, + }, + } + + path := "variables.file" + err := addUploadToOperations(request, upload, key, path) + require.Nil(t, err) + + require.Equal(t, request, expected) + }) + + t.Run("valid nested variable", func(t *testing.T) { + file, _ := os.Open("path/to/file") + request := ¶ms{ + Variables: map[string]interface{}{ + "req": []interface{}{ + map[string]interface{}{ + "file": nil, + }, + }, + }, + } + + upload := graphql.Upload{ + File: file, + Filename: "a.txt", + Size: int64(5), + } + + expected := ¶ms{ + Variables: map[string]interface{}{ + "req": []interface{}{ + map[string]interface{}{ + "file": upload, + }, + }, + }, + } + + path := "variables.req.0.file" + err := addUploadToOperations(request, upload, key, path) + require.Nil(t, err) + + require.Equal(t, request, expected) + }) +} + +type file struct { + mapKey string + name string + content string +} + +func createUploadRequest(t *testing.T, operations, mapData string, files []file) *http.Request { + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + + err := bodyWriter.WriteField("operations", operations) + require.NoError(t, err) + + err = bodyWriter.WriteField("map", mapData) + require.NoError(t, err) + + for i := range files { + ff, err := bodyWriter.CreateFormFile(files[i].mapKey, files[i].name) + require.NoError(t, err) + _, err = ff.Write([]byte(files[i].content)) + require.NoError(t, err) + } + err = bodyWriter.Close() + require.NoError(t, err) + + req, err := http.NewRequest("POST", "/graphql", bodyBuf) + require.NoError(t, err) + + req.Header.Set("Content-Type", bodyWriter.FormDataContentType()) + return req +} + func doRequest(handler http.Handler, method string, target string, body string) *httptest.ResponseRecorder { r := httptest.NewRequest(method, target, strings.NewReader(body)) w := httptest.NewRecorder() @@ -183,3 +645,79 @@ func doRequest(handler http.Handler, method string, target string, body string) handler.ServeHTTP(w, r) return w } + +func TestBytesRead(t *testing.T) { + t.Run("test concurrency", func(t *testing.T) { + // Test for the race detector, to verify a Read that doesn't yield any bytes + // is okay to use from multiple goroutines. This was our historic behavior. + // See golang.org/issue/7856 + r := bytesReader{s: &([]byte{})} + var wg sync.WaitGroup + for i := 0; i < 5; i++ { + wg.Add(2) + go func() { + defer wg.Done() + var buf [1]byte + r.Read(buf[:]) + }() + go func() { + defer wg.Done() + r.Read(nil) + }() + } + wg.Wait() + }) + + t.Run("fail to read if pointer is nil", func(t *testing.T) { + n, err := (&bytesReader{}).Read(nil) + require.Equal(t, 0, n) + require.NotNil(t, err) + require.Equal(t, "byte slice pointer is nil", err.Error()) + }) + + t.Run("read using buffer", func(t *testing.T) { + data := []byte("0123456789") + r := bytesReader{s: &data} + + got := make([]byte, 0, 11) + buf := make([]byte, 1) + for { + n, err := r.Read(buf) + if n < 0 { + require.Fail(t, "unexpected bytes read size") + } + got = append(got, buf[:n]...) + if err != nil { + if err == io.EOF { + break + } + require.Fail(t, "unexpected error while reading", err.Error()) + } + } + require.Equal(t, "0123456789", string(got)) + }) + + t.Run("read updated pointer value", func(t *testing.T) { + data := []byte("0123456789") + pointer := &data + r := bytesReader{s: pointer} + data[2] = []byte("9")[0] + + got := make([]byte, 0, 11) + buf := make([]byte, 1) + for { + n, err := r.Read(buf) + if n < 0 { + require.Fail(t, "unexpected bytes read size") + } + got = append(got, buf[:n]...) + if err != nil { + if err == io.EOF { + break + } + require.Fail(t, "unexpected error while reading", err.Error()) + } + } + require.Equal(t, "0193456789", string(got)) + }) +} diff --git a/handler/mock.go b/handler/mock.go new file mode 100644 index 0000000000..3e70cf036b --- /dev/null +++ b/handler/mock.go @@ -0,0 +1,57 @@ +package handler + +import ( + "context" + + "github.com/99designs/gqlgen/graphql" + "github.com/vektah/gqlparser" + "github.com/vektah/gqlparser/ast" +) + +type executableSchemaMock struct { + MutationFunc func(ctx context.Context, op *ast.OperationDefinition) *graphql.Response +} + +var _ graphql.ExecutableSchema = &executableSchemaMock{} + +func (e *executableSchemaMock) Schema() *ast.Schema { + return gqlparser.MustLoadSchema(&ast.Source{Input: ` + schema { query: Query, mutation: Mutation } + type Query { + empty: String! + } + scalar Upload + type File { + id: Int! + } + input UploadFile { + id: Int! + file: Upload! + } + type Mutation { + singleUpload(file: Upload!): File! + singleUploadWithPayload(req: UploadFile!): File! + multipleUpload(files: [Upload!]!): [File!]! + multipleUploadWithPayload(req: [UploadFile!]!): [File!]! + } + `}) +} + +func (e *executableSchemaMock) Complexity(typeName, field string, childComplexity int, args map[string]interface{}) (int, bool) { + return 0, false +} + +func (e *executableSchemaMock) Query(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + return graphql.ErrorResponse(ctx, "queries are not supported") +} + +func (e *executableSchemaMock) Mutation(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + return e.MutationFunc(ctx, op) +} + +func (e *executableSchemaMock) Subscription(ctx context.Context, op *ast.OperationDefinition) func() *graphql.Response { + return func() *graphql.Response { + <-ctx.Done() + return nil + } +}