From aae91e6d2854757e6f5fd8bdccb232bc63c31c9c Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sun, 18 Mar 2018 16:30:30 +1100 Subject: [PATCH 1/3] small fixes to entry point --- main.go | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/main.go b/main.go index dda727ef074..8c68ded8e87 100644 --- a/main.go +++ b/main.go @@ -17,7 +17,7 @@ import ( "golang.org/x/tools/imports" ) -var output = flag.String("out", "-", "the file to write to, - for stdout") +var output = flag.String("out", "generated.go", "the file to write to") var models = flag.String("models", "models_gen.go", "the file to write the models to") var schemaFilename = flag.String("schema", "schema.graphql", "the graphql schema to generate types from") var typemap = flag.String("typemap", "", "a json map going from graphql to golang types") @@ -49,15 +49,17 @@ func main() { os.Exit(1) } - if *output != "-" { - _ = syscall.Unlink(*output) - } + _ = syscall.Unlink(*output) _ = syscall.Unlink(*models) types := loadTypeMap() modelsBuild := codegen.Models(schema, types, dirName()) if len(modelsBuild.Models) > 0 { + if *packageName != "" { + modelsBuild.PackageName = *packageName + } + buf, err := templates.Run("models.gotpl", modelsBuild) if err != nil { fmt.Fprintf(os.Stderr, "unable to generate code: "+err.Error()) @@ -102,20 +104,17 @@ func gofmt(filename string, b []byte) []byte { } func write(filename string, b []byte) { - if filename == "-" { - fmt.Println(string(gofmt(filename, b))) - } else { - err := os.MkdirAll(filepath.Dir(filename), 0755) - if err != nil { - fmt.Fprintln(os.Stderr, "failed to create directory: ", err.Error()) - os.Exit(1) - } + fmt.Println(filename) + err := os.MkdirAll(filepath.Dir(filename), 0755) + if err != nil { + fmt.Fprintln(os.Stderr, "failed to create directory: ", err.Error()) + os.Exit(1) + } - err = ioutil.WriteFile(filename, gofmt(filename, b), 0644) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to write %s: %s", filename, err.Error()) - os.Exit(1) - } + err = ioutil.WriteFile(filename, gofmt(filename, b), 0644) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to write %s: %s", filename, err.Error()) + os.Exit(1) } } From 34689707c2e77d58c1da08cc11c7e42ddda1be77 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sun, 18 Mar 2018 16:42:48 +1100 Subject: [PATCH 2/3] detect correct FK type --- codegen/models_build.go | 20 ++++++++++++++++---- codegen/type.go | 3 ++- example/dataloader/models_gen.go | 8 +++----- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/codegen/models_build.go b/codegen/models_build.go index 317a4e7360f..278419f6207 100644 --- a/codegen/models_build.go +++ b/codegen/models_build.go @@ -19,13 +19,13 @@ func buildModels(types NamedTypes, s *schema.Schema, prog *loader.Program) []Mod if obj.Root || obj.GoType != "" { continue } - model = obj2Model(obj) + model = obj2Model(s, obj) case *schema.InputObject: obj := buildInput(types, typ) if obj.GoType != "" { continue } - model = obj2Model(obj) + model = obj2Model(s, obj) case *schema.Interface, *schema.Union: intf := buildInterface(types, typ, prog) if intf.GoType != "" { @@ -46,7 +46,7 @@ func buildModels(types NamedTypes, s *schema.Schema, prog *loader.Program) []Mod return models } -func obj2Model(obj *Object) Model { +func obj2Model(s *schema.Schema, obj *Object) Model { model := Model{ NamedType: obj.NamedType, Fields: []ModelField{}, @@ -66,9 +66,21 @@ func obj2Model(obj *Object) Model { } } else if mf.IsInput { mf.GoVarName = ucFirst(field.GQLName) + } else if mf.IsSlice() { + // one to many, we don't need a prop, we need a resolver } else { mf.GoFKName = ucFirst(field.GQLName) + "ID" - mf.GoFKType = "int" // todo: use schema to determine type of id? + mf.GoFKType = "string" + + if obj, ok := s.Types[field.GQLType].(*schema.Object); ok { + for _, f := range obj.Fields { + if strings.EqualFold(f.Name, "id") { + if strings.Contains(f.Type.String(), "Int") { + mf.GoFKType = "int" + } + } + } + } } model.Fields = append(model.Fields, mf) diff --git a/codegen/type.go b/codegen/type.go index c16495e29ad..36adb6b4233 100644 --- a/codegen/type.go +++ b/codegen/type.go @@ -70,7 +70,8 @@ func (t *Type) StripPtr() { } func (t Type) IsSlice() bool { - return len(t.Modifiers) > 0 && t.Modifiers[0] == modList + return len(t.Modifiers) > 0 && t.Modifiers[0] == modList || + len(t.Modifiers) > 1 && t.Modifiers[0] == modPtr && t.Modifiers[1] == modList } func (t NamedType) IsMarshaled() bool { diff --git a/example/dataloader/models_gen.go b/example/dataloader/models_gen.go index 2101ea3474a..931e5d777ab 100644 --- a/example/dataloader/models_gen.go +++ b/example/dataloader/models_gen.go @@ -15,14 +15,12 @@ type Customer struct { ID int Name string AddressID int - OrdersID int } type Item struct { Name string } type Order struct { - ID int - Date time.Time - Amount float64 - ItemsID int + ID int + Date time.Time + Amount float64 } From 0612395188a71f16ac079c425380a29f07370389 Mon Sep 17 00:00:00 2001 From: Adam Scarr Date: Sun, 18 Mar 2018 17:03:31 +1100 Subject: [PATCH 3/3] Update the tutorial --- docs/content/tutorial/getting-started.md | 62 +++++++++++++----------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/docs/content/tutorial/getting-started.md b/docs/content/tutorial/getting-started.md index a89f72ba06c..787905e09da 100644 --- a/docs/content/tutorial/getting-started.md +++ b/docs/content/tutorial/getting-started.md @@ -13,6 +13,7 @@ The aim for this tutorial is to build a "todo" graphql server that can: - create new todos - mark off todos as they are completed +You can find the finished code for this tutorial [here](https://github.com/vektah/gqlgen-tutorials/tree/master/gettingstarted) ## Install gqlgen @@ -54,13 +55,15 @@ type Mutation { Now that we have defined the shape of our data, and what actions can be taken we can ask gqlgen to convert the schema into code: ```bash -gqlgen -out generated.go -package main +mkdir graph +cd graph +gqlgen -schema ../schema.graphql ``` gqlgen should have created two new files `generated.go` and `models_gen.go`. If we take a peek in both we can see what the server has generated: ```go -// generated.go +// graph/generated.go func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { return &executableSchema{resolvers} } @@ -71,7 +74,7 @@ type Resolvers interface { Todo_user(ctx context.Context, it *Todo) (User, error) } -// models_gen.go +// graph/models_gen.go type Todo struct { ID string Text string @@ -96,23 +99,19 @@ directly. Finally, we get to write some code! ```go -// main.go -package main +// graph/graph.go +package graph import ( "context" "fmt" - "log" "math/rand" - "net/http" - - "github.com/vektah/gqlgen/handler" ) type MyApp struct { todos []Todo } - + func (a *MyApp) Query_todos(ctx context.Context) ([]Todo, error) { return a.todos, nil } @@ -127,21 +126,33 @@ func (a *MyApp) Mutation_createTodo(ctx context.Context, text string) (Todo, err return todo, nil } - func (a *MyApp) Todo_user(ctx context.Context, it *Todo) (User, error) { return User{ID: it.UserID, Name: "user " + it.UserID}, nil } +``` + +```go +// main.go +package main + +import ( + "fmt" + "log" + "net/http" + + "github.com/vektah/gqlgen-tutorials/gettingstarted/graph" + "github.com/vektah/gqlgen/handler" +) func main() { - app := &MyApp{ - todos: []Todo{}, // this would normally be a reference to the db - } + app := &graph.MyApp{} http.Handle("/", handler.Playground("Todo", "/query")) - http.Handle("/query", handler.GraphQL(MakeExecutableSchema(app))) + http.Handle("/query", handler.GraphQL(graph.MakeExecutableSchema(app))) fmt.Println("Listening on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) } + ``` We now have a working server, to start it: @@ -175,33 +186,26 @@ query findTodos { ## Customizing the models Generated models are nice to get moving quickly, but you probably want control over them at some point. To do that -create a types.json, eg: +create a `graph/types.json`: ```json { - "Todo": "github.com/vektah/gettingstarted.Todo" + "User": "github.com/vektah/gqlgen-tutorials/gettingstarted/graph.User" } ``` and create the model yourself: ```go -type Todo struct { - ID string - Text string - done bool - userID string // I've made userID private now. +// graph/graph.go +type User struct { + ID string + Name string } - -// lets define a getter too. it could also return an error if we needed. -func (t Todo) Done() bool { - return t.done -} - ``` then regenerate, this time specifying the type map: ```bash -gqlgen -out generated.go -package main -typemap types.json +gqlgen -typemap types.json -schema ../schema.graphql ``` gqlgen will look at the user defined types and match the fields up finding fields and functions by matching names.