diff --git a/.gometalinter.json b/.gometalinter.json index b8162c837ad..7e50f3fbf91 100644 --- a/.gometalinter.json +++ b/.gometalinter.json @@ -3,7 +3,7 @@ "Deadline": "5m", "Linters": { "errcheck": { - "Command": "errcheck -abspath -ignore '[rR]ead|[wW]rite|Close'", + "Command": "errcheck -abspath -ignore '[rR]ead|[wW]rite|Close|[fF]print'", "Pattern": "PATH:LINE:COL:MESSAGE", "InstallFrom": "github.com/kisielk/errcheck", "PartitionStrategy": "packages" diff --git a/codegen/build.go b/codegen/build.go index 9319c4448c4..ad6f68bd319 100644 --- a/codegen/build.go +++ b/codegen/build.go @@ -57,7 +57,7 @@ func (cfg *Config) models() (*ModelBuild, error) { func (cfg *Config) bind() (*Build, error) { namedTypes := cfg.buildNamedTypes() - prog, err := cfg.loadProgram(namedTypes, false) + prog, err := cfg.loadProgram(namedTypes, true) if err != nil { return nil, errors.Wrap(err, "loading failed") } @@ -122,6 +122,13 @@ func (cfg *Config) bind() (*Build, error) { return b, nil } +func (cfg *Config) validate() error { + namedTypes := cfg.buildNamedTypes() + + _, err := cfg.loadProgram(namedTypes, false) + return err +} + func (cfg *Config) loadProgram(namedTypes NamedTypes, allowErrors bool) (*loader.Program, error) { conf := loader.Config{} if allowErrors { diff --git a/codegen/codegen.go b/codegen/codegen.go index 6c34eefaf69..d6758b3a20f 100644 --- a/codegen/codegen.go +++ b/codegen/codegen.go @@ -82,6 +82,11 @@ func Generate(cfg Config) error { if err = write(cfg.ExecFilename, buf.Bytes()); err != nil { return err } + + if err = cfg.validate(); err != nil { + return errors.Wrap(err, "validation failed") + } + return nil } diff --git a/example/todo/server/server.go b/example/todo/server/server.go index e44ec3f1b41..2955998a882 100644 --- a/example/todo/server/server.go +++ b/example/todo/server/server.go @@ -14,7 +14,7 @@ import ( func main() { http.Handle("/", handler.Playground("Todo", "/query")) http.Handle("/query", handler.GraphQL( - todo.MakeExecutableSchema(todo.New()), + todo.NewExecutableSchema(todo.New()), handler.RecoverFunc(func(ctx context.Context, err interface{}) error { // send this panic somewhere log.Print(err) diff --git a/example/todo/todo.go b/example/todo/todo.go index 25028035692..edcfee356e3 100644 --- a/example/todo/todo.go +++ b/example/todo/todo.go @@ -10,13 +10,8 @@ import ( "github.com/mitchellh/mapstructure" ) -type todoResolver struct { - todos []Todo - lastID int -} - -func New() *todoResolver { - return &todoResolver{ +func New() *resolvers { + return &resolvers{ todos: []Todo{ {ID: 1, Text: "A todo not to forget", Done: false}, {ID: 2, Text: "This is the most important", Done: false}, @@ -26,7 +21,22 @@ func New() *todoResolver { } } -func (r *todoResolver) MyQuery_todo(ctx context.Context, id int) (*Todo, error) { +type resolvers struct { + todos []Todo + lastID int +} + +func (r *resolvers) MyQuery() MyQueryResolver { + return (*QueryResolver)(r) +} + +func (r *resolvers) MyMutation() MyMutationResolver { + return (*MutationResolver)(r) +} + +type QueryResolver resolvers + +func (r *QueryResolver) Todo(ctx context.Context, id int) (*Todo, error) { time.Sleep(220 * time.Millisecond) if id == 666 { @@ -41,18 +51,20 @@ func (r *todoResolver) MyQuery_todo(ctx context.Context, id int) (*Todo, error) return nil, errors.New("not found") } -func (r *todoResolver) MyQuery_lastTodo(ctx context.Context) (*Todo, error) { +func (r *QueryResolver) LastTodo(ctx context.Context) (*Todo, error) { if len(r.todos) == 0 { return nil, errors.New("not found") } return &r.todos[len(r.todos)-1], nil } -func (r *todoResolver) MyQuery_todos(ctx context.Context) ([]Todo, error) { +func (r *QueryResolver) Todos(ctx context.Context) ([]Todo, error) { return r.todos, nil } -func (r *todoResolver) MyMutation_createTodo(ctx context.Context, todo TodoInput) (Todo, error) { +type MutationResolver resolvers + +func (r *MutationResolver) CreateTodo(ctx context.Context, todo TodoInput) (Todo, error) { newID := r.id() newTodo := Todo{ @@ -69,9 +81,7 @@ func (r *todoResolver) MyMutation_createTodo(ctx context.Context, todo TodoInput return newTodo, nil } -// this example uses a map instead of a struct for the change set. this scales updating keys on large objects where -// most properties are optional, and if unspecified the existing value should be kept. -func (r *todoResolver) MyMutation_updateTodo(ctx context.Context, id int, changes map[string]interface{}) (*Todo, error) { +func (r *MutationResolver) UpdateTodo(ctx context.Context, id int, changes map[string]interface{}) (*Todo, error) { var affectedTodo *Todo for i := 0; i < len(r.todos); i++ { @@ -93,7 +103,7 @@ func (r *todoResolver) MyMutation_updateTodo(ctx context.Context, id int, change return affectedTodo, nil } -func (r *todoResolver) id() int { +func (r *MutationResolver) id() int { r.lastID++ return r.lastID } diff --git a/example/todo/todo_test.go b/example/todo/todo_test.go index ab607e02b4d..bdb9e9d4542 100644 --- a/example/todo/todo_test.go +++ b/example/todo/todo_test.go @@ -11,7 +11,7 @@ import ( ) func TestTodo(t *testing.T) { - srv := httptest.NewServer(handler.GraphQL(MakeExecutableSchema(New()))) + srv := httptest.NewServer(handler.GraphQL(NewExecutableSchema(New()))) c := client.New(srv.URL) t.Run("create a new todo", func(t *testing.T) {