From 62a18ff1ebd8ff1d4b370f217b03f63ac7641d6f Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sat, 17 Feb 2018 18:38:24 +1100 Subject: [PATCH] Update generator to build a new ExecutableSchema interface --- codegen/object.go | 2 +- example/dataloader/generated.go | 72 +++++++--------- example/dataloader/server/server.go | 2 +- example/scalars/generated.go | 78 +++++++---------- example/scalars/scalar_test.go | 2 +- example/scalars/server/server.go | 4 +- example/starwars/generated.go | 126 +++++++++++----------------- example/starwars/server/server.go | 4 +- example/starwars/starwars_test.go | 2 +- example/todo/generated.go | 87 ++++++++----------- example/todo/server/server.go | 4 +- example/todo/todo_test.go | 2 +- templates/args.go | 3 +- templates/file.go | 102 ++++++++++++---------- templates/object.go | 56 ++++++++++++- 15 files changed, 271 insertions(+), 275 deletions(-) diff --git a/codegen/object.go b/codegen/object.go index 2b773c8ba9..cf001b95d8 100644 --- a/codegen/object.go +++ b/codegen/object.go @@ -78,7 +78,7 @@ func (f *Field) ResolverDeclaration() string { result := f.Signature() if f.Object.Stream { - result = "chan<-" + result + result = "<-chan " + result } res += fmt.Sprintf(") (%s, error)", result) diff --git a/example/dataloader/generated.go b/example/dataloader/generated.go index 707b0e1919..5889352f3c 100644 --- a/example/dataloader/generated.go +++ b/example/dataloader/generated.go @@ -4,7 +4,6 @@ package dataloader import ( context "context" - io "io" strconv "strconv" sync "sync" @@ -13,7 +12,6 @@ import ( introspection "github.com/vektah/gqlgen/neelance/introspection" query "github.com/vektah/gqlgen/neelance/query" schema "github.com/vektah/gqlgen/neelance/schema" - validation "github.com/vektah/gqlgen/neelance/validation" ) type Resolvers interface { @@ -24,49 +22,38 @@ type Resolvers interface { Query_customers(ctx context.Context) ([]Customer, error) } -func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[string]interface{}, io.Writer) []*errors.QueryError { - return func(ctx context.Context, document string, operationName string, variables map[string]interface{}, w io.Writer) []*errors.QueryError { - doc, qErr := query.Parse(document) - if qErr != nil { - return []*errors.QueryError{qErr} - } - - errs := validation.Validate(parsedSchema, doc) - if len(errs) != 0 { - return errs - } +func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { + return &executableSchema{resolvers} +} - op, err := doc.GetOperation(operationName) - if err != nil { - return []*errors.QueryError{errors.Errorf("%s", err)} - } +type executableSchema struct { + resolvers Resolvers +} - c := executionContext{ - resolvers: resolvers, - variables: variables, - doc: doc, - ctx: ctx, - } +func (e *executableSchema) Schema() *schema.Schema { + return parsedSchema +} - var data graphql.Marshaler - if op.Type == query.Query { - data = c._query(op.Selections, nil) - } else { - return []*errors.QueryError{errors.Errorf("unsupported operation type")} - } +func (e *executableSchema) Query(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} - c.wg.Wait() + data := ec._query(op.Selections, nil) + ec.wg.Wait() - result := &graphql.OrderedMap{} - result.Add("data", data) + return &graphql.Response{ + Data: data, + Errors: ec.Errors, + } +} - if len(c.Errors) > 0 { - result.Add("errors", graphql.MarshalErrors(c.Errors)) - } +func (e *executableSchema) Mutation(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + return &graphql.Response{Errors: []*errors.QueryError{{Message: "mutations are not supported"}}} +} - result.MarshalGQL(w) - return nil - } +func (e *executableSchema) Subscription(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) <-chan *graphql.Response { + events := make(chan *graphql.Response, 1) + events <- &graphql.Response{Errors: []*errors.QueryError{{Message: "subscriptions are not supported"}}} + return events } type executionContext struct { @@ -294,8 +281,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["name"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -647,8 +633,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ if tmp, ok := field.Args["includeDeprecated"]; ok { tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -701,8 +686,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ if tmp, ok := field.Args["includeDeprecated"]; ok { tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } diff --git a/example/dataloader/server/server.go b/example/dataloader/server/server.go index 9e2c5e0468..0a47ac44ef 100644 --- a/example/dataloader/server/server.go +++ b/example/dataloader/server/server.go @@ -9,7 +9,7 @@ import ( ) func main() { - http.Handle("/", handler.GraphiQL("Dataloader", "/query")) + http.Handle("/", handler.Playground("Dataloader", "/query")) http.Handle("/query", dataloader.LoaderMiddleware(handler.GraphQL(dataloader.NewExecutor(&dataloader.Resolver{})))) diff --git a/example/scalars/generated.go b/example/scalars/generated.go index 0e01d44f58..2a67aa5b59 100644 --- a/example/scalars/generated.go +++ b/example/scalars/generated.go @@ -4,7 +4,6 @@ package scalars import ( context "context" - io "io" strconv "strconv" sync "sync" @@ -13,7 +12,6 @@ import ( introspection "github.com/vektah/gqlgen/neelance/introspection" query "github.com/vektah/gqlgen/neelance/query" schema "github.com/vektah/gqlgen/neelance/schema" - validation "github.com/vektah/gqlgen/neelance/validation" ) type Resolvers interface { @@ -21,49 +19,38 @@ type Resolvers interface { Query_search(ctx context.Context, input SearchArgs) ([]User, error) } -func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[string]interface{}, io.Writer) []*errors.QueryError { - return func(ctx context.Context, document string, operationName string, variables map[string]interface{}, w io.Writer) []*errors.QueryError { - doc, qErr := query.Parse(document) - if qErr != nil { - return []*errors.QueryError{qErr} - } - - errs := validation.Validate(parsedSchema, doc) - if len(errs) != 0 { - return errs - } +func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { + return &executableSchema{resolvers} +} - op, err := doc.GetOperation(operationName) - if err != nil { - return []*errors.QueryError{errors.Errorf("%s", err)} - } +type executableSchema struct { + resolvers Resolvers +} - c := executionContext{ - resolvers: resolvers, - variables: variables, - doc: doc, - ctx: ctx, - } +func (e *executableSchema) Schema() *schema.Schema { + return parsedSchema +} - var data graphql.Marshaler - if op.Type == query.Query { - data = c._query(op.Selections, nil) - } else { - return []*errors.QueryError{errors.Errorf("unsupported operation type")} - } +func (e *executableSchema) Query(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} - c.wg.Wait() + data := ec._query(op.Selections, nil) + ec.wg.Wait() - result := &graphql.OrderedMap{} - result.Add("data", data) + return &graphql.Response{ + Data: data, + Errors: ec.Errors, + } +} - if len(c.Errors) > 0 { - result.Add("errors", graphql.MarshalErrors(c.Errors)) - } +func (e *executableSchema) Mutation(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + return &graphql.Response{Errors: []*errors.QueryError{{Message: "mutations are not supported"}}} +} - result.MarshalGQL(w) - return nil - } +func (e *executableSchema) Subscription(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) <-chan *graphql.Response { + events := make(chan *graphql.Response, 1) + events <- &graphql.Response{Errors: []*errors.QueryError{{Message: "subscriptions are not supported"}}} + return events } type executionContext struct { @@ -93,8 +80,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["id"]; ok { tmp2, err := graphql.UnmarshalID(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -118,8 +104,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["input"]; ok { tmp2, err := UnmarshalSearchArgs(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -153,8 +138,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["name"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -543,8 +527,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ if tmp, ok := field.Args["includeDeprecated"]; ok { tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -597,8 +580,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ if tmp, ok := field.Args["includeDeprecated"]; ok { tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } diff --git a/example/scalars/scalar_test.go b/example/scalars/scalar_test.go index 340e8aa527..12df7b5311 100644 --- a/example/scalars/scalar_test.go +++ b/example/scalars/scalar_test.go @@ -19,7 +19,7 @@ type RawUser struct { } func TestScalars(t *testing.T) { - srv := httptest.NewServer(handler.GraphQL(NewExecutor(&Resolver{}))) + srv := httptest.NewServer(handler.GraphQL(MakeExecutableSchema(&Resolver{}))) c := client.New(srv.URL) var resp struct { diff --git a/example/scalars/server/server.go b/example/scalars/server/server.go index df2c1a24ea..22805f2224 100644 --- a/example/scalars/server/server.go +++ b/example/scalars/server/server.go @@ -9,8 +9,8 @@ import ( ) func main() { - http.Handle("/", handler.GraphiQL("Starwars", "/query")) - http.Handle("/query", handler.GraphQL(scalars.NewExecutor(&scalars.Resolver{}))) + http.Handle("/", handler.Playground("Starwars", "/query")) + http.Handle("/query", handler.GraphQL(scalars.MakeExecutableSchema(&scalars.Resolver{}))) log.Fatal(http.ListenAndServe(":8084", nil)) } diff --git a/example/starwars/generated.go b/example/starwars/generated.go index 5270f3e0d7..93eb01c957 100644 --- a/example/starwars/generated.go +++ b/example/starwars/generated.go @@ -5,7 +5,6 @@ package starwars import ( context "context" fmt "fmt" - io "io" strconv "strconv" sync "sync" time "time" @@ -15,7 +14,6 @@ import ( introspection "github.com/vektah/gqlgen/neelance/introspection" query "github.com/vektah/gqlgen/neelance/query" schema "github.com/vektah/gqlgen/neelance/schema" - validation "github.com/vektah/gqlgen/neelance/validation" ) type Resolvers interface { @@ -40,53 +38,48 @@ type Resolvers interface { Query_starship(ctx context.Context, id string) (*Starship, error) } -func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[string]interface{}, io.Writer) []*errors.QueryError { - return func(ctx context.Context, document string, operationName string, variables map[string]interface{}, w io.Writer) []*errors.QueryError { - doc, qErr := query.Parse(document) - if qErr != nil { - return []*errors.QueryError{qErr} - } +func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { + return &executableSchema{resolvers} +} - errs := validation.Validate(parsedSchema, doc) - if len(errs) != 0 { - return errs - } +type executableSchema struct { + resolvers Resolvers +} - op, err := doc.GetOperation(operationName) - if err != nil { - return []*errors.QueryError{errors.Errorf("%s", err)} - } +func (e *executableSchema) Schema() *schema.Schema { + return parsedSchema +} - c := executionContext{ - resolvers: resolvers, - variables: variables, - doc: doc, - ctx: ctx, - } +func (e *executableSchema) Query(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} - var data graphql.Marshaler - if op.Type == query.Query { - data = c._query(op.Selections, nil) - } else if op.Type == query.Mutation { - data = c._mutation(op.Selections, nil) - } else { - return []*errors.QueryError{errors.Errorf("unsupported operation type")} - } + data := ec._query(op.Selections, nil) + ec.wg.Wait() - c.wg.Wait() + return &graphql.Response{ + Data: data, + Errors: ec.Errors, + } +} - result := &graphql.OrderedMap{} - result.Add("data", data) +func (e *executableSchema) Mutation(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} - if len(c.Errors) > 0 { - result.Add("errors", graphql.MarshalErrors(c.Errors)) - } + data := ec._mutation(op.Selections, nil) + ec.wg.Wait() - result.MarshalGQL(w) - return nil + return &graphql.Response{ + Data: data, + Errors: ec.Errors, } } +func (e *executableSchema) Subscription(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) <-chan *graphql.Response { + events := make(chan *graphql.Response, 1) + events <- &graphql.Response{Errors: []*errors.QueryError{{Message: "subscriptions are not supported"}}} + return events +} + type executionContext struct { errors.Builder resolvers Resolvers @@ -140,8 +133,7 @@ func (ec *executionContext) _droid(sel []query.Selection, it *Droid) graphql.Mar if tmp, ok := field.Args["first"]; ok { tmp2, err := graphql.UnmarshalInt(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = &tmp2 } @@ -149,8 +141,7 @@ func (ec *executionContext) _droid(sel []query.Selection, it *Droid) graphql.Mar if tmp, ok := field.Args["after"]; ok { tmp2, err := graphql.UnmarshalID(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg1 = &tmp2 } @@ -307,8 +298,7 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) graphql.Mar if tmp, ok := field.Args["unit"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -342,8 +332,7 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) graphql.Mar if tmp, ok := field.Args["first"]; ok { tmp2, err := graphql.UnmarshalInt(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = &tmp2 } @@ -351,8 +340,7 @@ func (ec *executionContext) _human(sel []query.Selection, it *Human) graphql.Mar if tmp, ok := field.Args["after"]; ok { tmp2, err := graphql.UnmarshalID(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg1 = &tmp2 } @@ -421,8 +409,7 @@ func (ec *executionContext) _mutation(sel []query.Selection, it *interface{}) gr if tmp, ok := field.Args["episode"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -430,8 +417,7 @@ func (ec *executionContext) _mutation(sel []query.Selection, it *interface{}) gr if tmp, ok := field.Args["review"]; ok { tmp2, err := UnmarshalReviewInput(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg1 = tmp2 } @@ -505,8 +491,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["episode"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = &tmp2 } @@ -526,8 +511,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["episode"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -535,8 +519,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["since"]; ok { tmp2, err := graphql.UnmarshalTime(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg1 = &tmp2 } @@ -562,8 +545,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["text"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -589,8 +571,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["id"]; ok { tmp2, err := graphql.UnmarshalID(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -610,8 +591,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["id"]; ok { tmp2, err := graphql.UnmarshalID(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -635,8 +615,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["id"]; ok { tmp2, err := graphql.UnmarshalID(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -660,8 +639,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["id"]; ok { tmp2, err := graphql.UnmarshalID(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -693,8 +671,7 @@ func (ec *executionContext) _query(sel []query.Selection, it *interface{}) graph if tmp, ok := field.Args["name"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -776,8 +753,7 @@ func (ec *executionContext) _starship(sel []query.Selection, it *Starship) graph if tmp, ok := field.Args["unit"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -1142,8 +1118,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ if tmp, ok := field.Args["includeDeprecated"]; ok { tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -1196,8 +1171,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ if tmp, ok := field.Args["includeDeprecated"]; ok { tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } diff --git a/example/starwars/server/server.go b/example/starwars/server/server.go index fcf72aaaca..01f591728a 100644 --- a/example/starwars/server/server.go +++ b/example/starwars/server/server.go @@ -9,8 +9,8 @@ import ( ) func main() { - http.Handle("/", handler.GraphiQL("Starwars", "/query")) - http.Handle("/query", handler.GraphQL(starwars.NewExecutor(starwars.NewResolver()))) + http.Handle("/", handler.Playground("Starwars", "/query")) + http.Handle("/query", handler.GraphQL(starwars.MakeExecutableSchema(starwars.NewResolver()))) log.Fatal(http.ListenAndServe(":8080", nil)) } diff --git a/example/starwars/starwars_test.go b/example/starwars/starwars_test.go index 702095abf3..04e9646265 100644 --- a/example/starwars/starwars_test.go +++ b/example/starwars/starwars_test.go @@ -11,7 +11,7 @@ import ( ) func TestStarwars(t *testing.T) { - srv := httptest.NewServer(handler.GraphQL(NewExecutor(NewResolver()))) + srv := httptest.NewServer(handler.GraphQL(MakeExecutableSchema(NewResolver()))) c := client.New(srv.URL) t.Run("Lukes starships", func(t *testing.T) { diff --git a/example/todo/generated.go b/example/todo/generated.go index a416c9b826..0324539d2c 100644 --- a/example/todo/generated.go +++ b/example/todo/generated.go @@ -4,7 +4,6 @@ package todo import ( context "context" - io "io" strconv "strconv" sync "sync" @@ -13,7 +12,6 @@ import ( introspection "github.com/vektah/gqlgen/neelance/introspection" query "github.com/vektah/gqlgen/neelance/query" schema "github.com/vektah/gqlgen/neelance/schema" - validation "github.com/vektah/gqlgen/neelance/validation" ) type Resolvers interface { @@ -24,53 +22,48 @@ type Resolvers interface { MyQuery_todos(ctx context.Context) ([]Todo, error) } -func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[string]interface{}, io.Writer) []*errors.QueryError { - return func(ctx context.Context, document string, operationName string, variables map[string]interface{}, w io.Writer) []*errors.QueryError { - doc, qErr := query.Parse(document) - if qErr != nil { - return []*errors.QueryError{qErr} - } +func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { + return &executableSchema{resolvers} +} - errs := validation.Validate(parsedSchema, doc) - if len(errs) != 0 { - return errs - } +type executableSchema struct { + resolvers Resolvers +} - op, err := doc.GetOperation(operationName) - if err != nil { - return []*errors.QueryError{errors.Errorf("%s", err)} - } +func (e *executableSchema) Schema() *schema.Schema { + return parsedSchema +} - c := executionContext{ - resolvers: resolvers, - variables: variables, - doc: doc, - ctx: ctx, - } +func (e *executableSchema) Query(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} - var data graphql.Marshaler - if op.Type == query.Query { - data = c._myQuery(op.Selections, nil) - } else if op.Type == query.Mutation { - data = c._myMutation(op.Selections, nil) - } else { - return []*errors.QueryError{errors.Errorf("unsupported operation type")} - } + data := ec._myQuery(op.Selections, nil) + ec.wg.Wait() - c.wg.Wait() + return &graphql.Response{ + Data: data, + Errors: ec.Errors, + } +} - result := &graphql.OrderedMap{} - result.Add("data", data) +func (e *executableSchema) Mutation(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} - if len(c.Errors) > 0 { - result.Add("errors", graphql.MarshalErrors(c.Errors)) - } + data := ec._myMutation(op.Selections, nil) + ec.wg.Wait() - result.MarshalGQL(w) - return nil + return &graphql.Response{ + Data: data, + Errors: ec.Errors, } } +func (e *executableSchema) Subscription(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) <-chan *graphql.Response { + events := make(chan *graphql.Response, 1) + events <- &graphql.Response{Errors: []*errors.QueryError{{Message: "subscriptions are not supported"}}} + return events +} + type executionContext struct { errors.Builder resolvers Resolvers @@ -98,8 +91,7 @@ func (ec *executionContext) _myMutation(sel []query.Selection, it *interface{}) if tmp, ok := field.Args["text"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -115,8 +107,7 @@ func (ec *executionContext) _myMutation(sel []query.Selection, it *interface{}) if tmp, ok := field.Args["id"]; ok { tmp2, err := graphql.UnmarshalInt(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -161,8 +152,7 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) gra if tmp, ok := field.Args["id"]; ok { tmp2, err := graphql.UnmarshalInt(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -228,8 +218,7 @@ func (ec *executionContext) _myQuery(sel []query.Selection, it *interface{}) gra if tmp, ok := field.Args["name"]; ok { tmp2, err := graphql.UnmarshalString(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -614,8 +603,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ if tmp, ok := field.Args["includeDeprecated"]; ok { tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } @@ -668,8 +656,7 @@ func (ec *executionContext) ___Type(sel []query.Selection, it *introspection.Typ if tmp, ok := field.Args["includeDeprecated"]; ok { tmp2, err := graphql.UnmarshalBoolean(tmp) if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg0 = tmp2 } diff --git a/example/todo/server/server.go b/example/todo/server/server.go index 555ebf4345..f570e3ef1a 100644 --- a/example/todo/server/server.go +++ b/example/todo/server/server.go @@ -9,7 +9,7 @@ import ( ) func main() { - http.Handle("/", handler.GraphiQL("Todo", "/query")) - http.Handle("/query", handler.GraphQL(todo.NewExecutor(todo.New()))) + http.Handle("/", handler.Playground("Todo", "/query")) + http.Handle("/query", handler.GraphQL(todo.MakeExecutableSchema(todo.New()))) log.Fatal(http.ListenAndServe(":8081", nil)) } diff --git a/example/todo/todo_test.go b/example/todo/todo_test.go index 281013faad..10827ac4cb 100644 --- a/example/todo/todo_test.go +++ b/example/todo/todo_test.go @@ -12,7 +12,7 @@ import ( ) func TestTodo(t *testing.T) { - srv := httptest.NewServer(handler.GraphQL(NewExecutor(New()))) + srv := httptest.NewServer(handler.GraphQL(MakeExecutableSchema(New()))) c := client.New(srv.URL) t.Run("create a new todo", func(t *testing.T) { diff --git a/templates/args.go b/templates/args.go index 4e2b365425..2200c3d9cd 100644 --- a/templates/args.go +++ b/templates/args.go @@ -17,8 +17,7 @@ const argsTpl = ` if tmp, ok := field.Args[{{$arg.GQLName|quote}}]; ok { {{$arg.Unmarshal "tmp2" "tmp" }} if err != nil { - ec.Error(err) - continue + panic(err) // todo: fixme } arg{{$i}} = {{if $arg.Type.IsPtr}}&{{end}}tmp2 } diff --git a/templates/file.go b/templates/file.go index b601656544..1f92e74c79 100644 --- a/templates/file.go +++ b/templates/file.go @@ -20,56 +20,72 @@ type Resolvers interface { {{- end }} } -func NewExecutor(resolvers Resolvers) func(context.Context, string, string, map[string]interface{}, io.Writer) []*errors.QueryError { - return func(ctx context.Context, document string, operationName string, variables map[string]interface{}, w io.Writer) []*errors.QueryError { - doc, qErr := query.Parse(document) - if qErr != nil { - return []*errors.QueryError{qErr} - } - - errs := validation.Validate(parsedSchema, doc) - if len(errs) != 0 { - return errs - } +func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { + return &executableSchema{resolvers} +} - op, err := doc.GetOperation(operationName) - if err != nil { - return []*errors.QueryError{errors.Errorf("%s", err)} - } +type executableSchema struct { + resolvers Resolvers +} - c := executionContext{ - resolvers: resolvers, - variables: variables, - doc: doc, - ctx: ctx, - } +func (e *executableSchema) Schema() *schema.Schema { + return parsedSchema +} - var data graphql.Marshaler - if op.Type == query.Query { - data = c._{{.QueryRoot.GQLType|lcFirst}}(op.Selections, nil) - {{- if .MutationRoot}} - } else if op.Type == query.Mutation { - data = c._{{.MutationRoot.GQLType|lcFirst}}(op.Selections, nil) - {{- end}}{{ if .SubscriptionRoot}} - } else if op.Type == query.Subscription { - data = c._{{.SubscriptionRoot.GQLType|lcFirst}}(op.Selections, nil) - {{- end}} - } else { - return []*errors.QueryError{errors.Errorf("unsupported operation type")} +func (e *executableSchema) Query(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + {{- if .QueryRoot }} + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} + + data := ec._{{.QueryRoot.GQLType|lcFirst}}(op.Selections, nil) + ec.wg.Wait() + + return &graphql.Response{ + Data: data, + Errors: ec.Errors, } + {{- else }} + return &graphql.Response{Errors: []*errors.QueryError{ {Message: "queries are not supported"} }} + {{- end }} +} - c.wg.Wait() - - result := &graphql.OrderedMap{} - result.Add("data", data) - - if len(c.Errors) > 0 { - result.Add("errors", graphql.MarshalErrors(c.Errors)) +func (e *executableSchema) Mutation(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) *graphql.Response { + {{- if .MutationRoot }} + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} + + data := ec._{{.MutationRoot.GQLType|lcFirst}}(op.Selections, nil) + ec.wg.Wait() + + return &graphql.Response{ + Data: data, + Errors: ec.Errors, } + {{- else }} + return &graphql.Response{Errors: []*errors.QueryError{ {Message: "mutations are not supported"} }} + {{- end }} +} - result.MarshalGQL(w) - return nil - } +func (e *executableSchema) Subscription(ctx context.Context, doc *query.Document, variables map[string]interface{}, op *query.Operation) <-chan *graphql.Response { + {{- if .SubscriptionRoot }} + events := make(chan *graphql.Response, 10) + + ec := executionContext{resolvers: e.resolvers, variables: variables, doc: doc, ctx: ctx} + + go func() { + for data := range ec._{{.SubscriptionRoot.GQLType|lcFirst}}(op.Selections, nil) { + ec.wg.Wait() + events <- &graphql.Response{ + Data: data, + Errors: ec.Errors, + } + time.Sleep(20 * time.Millisecond) + } + }() + return events + {{- else }} + events := make(chan *graphql.Response, 1) + events<-&graphql.Response{Errors: []*errors.QueryError{ {Message: "subscriptions are not supported"} }} + return events + {{- end }} } type executionContext struct { diff --git a/templates/object.go b/templates/object.go index bc13c2fdb2..6bdcfdec64 100644 --- a/templates/object.go +++ b/templates/object.go @@ -7,6 +7,60 @@ const objectTpl = ` var {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}} // nolint: gocyclo, errcheck, gas, goconst +{{- if .Stream }} +func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, it *{{$object.FullName}}) <-chan graphql.Marshaler { + fields := graphql.CollectFields(ec.doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.variables) + + if len(fields) != 1 { + fmt.Errorf("must subscribe to exactly one stream") + return nil + } + + var field = fields[0] + channel := make(chan graphql.Marshaler, 1) + switch field.Name { + {{- range $field := $object.Fields }} + case "{{$field.GQLName}}": + {{- template "args" $field.Args }} + + {{- if $field.GoVarName }} + results := {{$field.GoVarName}} + {{- else if $field.GoMethodName }} + {{- if $field.NoErr }} + results := {{$field.GoMethodName}}({{ $field.CallArgs }}) + {{- else }} + results, err := {{$field.GoMethodName}}({{ $field.CallArgs }}) + if err != nil { + ec.Error(err) + return nil + } + {{- end }} + {{- else }} + results, err := ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }}) + if err != nil { + ec.Error(err) + return nil + } + {{- end }} + + go func() { + for res := range results { + var out graphql.OrderedMap + var messageRes graphql.Marshaler + {{ $field.WriteJson "messageRes" }} + out.Add(field.Alias, messageRes) + channel <- &out + } + }() + + {{- end }} + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + + return channel +} +{{- else }} func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, it *{{$object.FullName}}) graphql.Marshaler { fields := graphql.CollectFields(ec.doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.variables) out := graphql.NewOrderedMap(len(fields)) @@ -60,7 +114,7 @@ func (ec *executionContext) _{{$object.GQLType|lcFirst}}(sel []query.Selection, return out } - +{{- end }} {{- end}} `