From 601354b364911267763ac8536d9a738edf15a147 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Mon, 5 Nov 2018 13:32:38 -0800 Subject: [PATCH 01/10] Add blockquote breakout style --- docs/static/main.css | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/static/main.css b/docs/static/main.css index e4b06d2062..ed19dce501 100644 --- a/docs/static/main.css +++ b/docs/static/main.css @@ -392,3 +392,24 @@ strong { } } + + +blockquote { + background-color: #fffaf3; + border-left-color: rgba(0, 0, 0, 0.1); + border-left-width: 9px; + border-left-style: solid; + padding: 1em 20px 1em 11px; + margin-bottom: 1em; + margin-left: -20px; + margin-right: -20px; +} + +blockquote p { + margin-bottom: 0; +} + +/* Blockquote headings. */ +blockquote p:first-of-type { + font-weight: bold; +} From a36fffd213e5860a4d57604829811bc4fc4c0ec7 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Mon, 5 Nov 2018 16:37:33 -0800 Subject: [PATCH 02/10] Updated getting started with new no-binary approach --- docs/content/getting-started.md | 86 +++++++++++++++++---------------- docs/static/main.css | 28 ++++++++--- docs/static/syntax.css | 1 - 3 files changed, 64 insertions(+), 51 deletions(-) diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index fe7e3ae393..47414b2ee1 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -6,32 +6,54 @@ menu: main weight: -5 --- -## Goal +This tutorial will take you through the process of building a GraphQL server with gqlgen that can: -The aim for this tutorial is to build a "todo" graphql server that can: - - - get a list of all todos - - create new todos - - mark off todos as they are completed + - Return a list of todos + - 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 -Assuming you already have a working [go environment](https://golang.org/doc/install) you can simply go get: +This article uses [`dep`](https://github.com/golang/dep) to install gqlgen. [Follow the instructions for your environment](https://github.com/golang/dep) to install. + +Assuming you already have a working [Go environment](https://golang.org/doc/install), create a directory for the project in your `$GOPATH`: + +```sh +$ mkdir -p $GOPATH/src/github.com/[username]/gqlgen-todos +``` + +Add the following file to your project under `scripts/gqlgen.go`: + +```go +// +build ignore +package main + +import "github.com/99designs/gqlgen/cmd" + +func main() { + cmd.Execute() +} +``` + +Lastly, install dependencies: ```sh -go get -u github.com/99designs/gqlgen github.com/vektah/gorunpkg +$ dep ensure ``` +> Go Modules +> +> Currently `gqlgen` does not support Go Modules. This is due to the [`loader`](https://godoc.org/golang.org/x/tools/go/loader) package, that also does not yet support Go Modules. We are looking at solutions to this and the issue is tracked in Github. + ## Building the server ### Define the schema -gqlgen is a schema-first library, so before touching any code we write out the API we want using the graphql -[Schema Definition Language](http://graphql.org/learn/schema/). This usually goes into a file called schema.graphql +gqlgen is a schema-first library — before writing code, you describe your API using the GraphQL +[Schema Definition Language](http://graphql.org/learn/schema/). This usually goes into a file called `schema.graphql`: -`schema.graphql` ```graphql type Todo { id: ID! @@ -60,17 +82,17 @@ type Mutation { ``` ### Create the project skeleton + ```bash -$ gqlgen init -Exec "go run ./server/server.go" to start GraphQL server +$ go run scripts/gqlgen.go init ``` -This has created an empty skeleton with all files we need: +This has created an empty skeleton with all files you need: - - gqlgen.yml - The gqlgen config file, knobs for controlling the generated code. - - generated.go - The graphql execution runtime, the bulk of the generated code - - models_gen.go - Generated models required to build the graph. Often you will override these with models you write yourself. Still very useful for input types. - - resolver.go - This is where your application code lives. generated.go will call into this to get the data the user has requested. + - `gqlgen.yml` — The gqlgen config file, knobs for controlling the generated code. + - `generated.go` — The GraphQL execution runtime, the bulk of the generated code. + - `models_gen.go` — Generated models required to build the graph. Often you will override these with your own models. Still very useful for input types. + - `resolver.go` — This is where your application code lives. `generated.go` will call into this to get the data the user has requested. ### Create the database models @@ -88,7 +110,7 @@ type Todo struct { } ``` -And then tell gqlgen to use this new struct by adding this to the gqlgen.yml: +And then tell gqlgen to use this new struct by adding this to the `gqlgen.yml`: ```yaml models: Todo: @@ -97,7 +119,7 @@ models: and regenerate by running ```bash -$ gqlgen -v +$ go run scripts/gqlgen.go -v Unable to bind Todo.user to github.com/vektah/gqlgen-tutorials/gettingstarted.Todo no method named user no field named user @@ -242,28 +264,9 @@ query findTodos { ## Finishing touches -gqlgen is still unstable, and the APIs may change at any time. To prevent changes from ruining your day make sure -to lock your dependencies: - -*Note*: If you dont have dep installed yet, you can get it [here](https://github.com/golang/dep) - -First uninstall the global version we grabbed earlier. This is a good way to prevent version mismatch footguns. - -```bash -rm ~/go/bin/gqlgen -rm -rf ~/go/src/github.com/99designs/gqlgen -``` - -Next install gorunpkg, its kind of like npx but only searches vendor. - -```bash -dep init -dep ensure -``` - At the top of our resolvers.go a go generate command was added that looks like this: ```go -//go:generate gorunpkg github.com/99designs/gqlgen +//go:generate go run ./scripts/gqlgen.go ``` This magic comment tells `go generate` what command to run when we want to regenerate our code. to do so run: @@ -271,5 +274,4 @@ This magic comment tells `go generate` what command to run when we want to regen go generate ./... ``` -*gorunpkg* will build and run the version of gqlgen we just installed into vendor with dep. This makes sure that everyone working on your project generates code the same way regardless which binaries are installed in their gopath. - +You can now run this command every time you want to regenerate your GraphQL server. diff --git a/docs/static/main.css b/docs/static/main.css index ed19dce501..0a57099ad9 100644 --- a/docs/static/main.css +++ b/docs/static/main.css @@ -20,6 +20,11 @@ --color-code-text: #445; --color-code-background: #f5f9fc; + + --color-blockquote-background: #fffaf3; + --color-blockquote-highlight: rgba(0, 0, 0, 0.1); + + --margin-default: 15px; } html, body, div, span, applet, object, iframe, @@ -48,7 +53,7 @@ footer, header, hgroup, menu, nav, section { display: block; } ol, ul { - margin-bottom: 1em; + margin-bottom: var(--margin-default); list-style: disc; margin-left: 1.5em; } @@ -80,7 +85,7 @@ a { } a:hover { - border-bottom: 2px solid; + text-decoration: underline; } @media (min-width: 768px) { @@ -145,20 +150,20 @@ h1 { h2 { margin-top: 2em; - margin-bottom: 1em; + margin-bottom: var(--margin-default); font-size: 19px; font-weight: 700; } h3 { margin-top: 1.5em; - margin-bottom: 1em; + margin-bottom: var(--margin-default); font-size: 16px; font-weight: 500; } p { - margin-bottom: 1em; + margin-bottom: var(--margin-default); } nav { @@ -252,6 +257,9 @@ code { font-weight: 500; color: var(--color-code-text); background-color: var(--color-code-background); + border-radius: 3px; + font-size: 13px; + margin-bottom: var(--margin-default); } strong { @@ -395,12 +403,12 @@ strong { blockquote { - background-color: #fffaf3; - border-left-color: rgba(0, 0, 0, 0.1); + background-color: var(--color-blockquote-background); + border-left-color: var(--color-blockquote-highlight); border-left-width: 9px; border-left-style: solid; padding: 1em 20px 1em 11px; - margin-bottom: 1em; + margin-bottom: var(--margin-default); margin-left: -20px; margin-right: -20px; } @@ -413,3 +421,7 @@ blockquote p { blockquote p:first-of-type { font-weight: bold; } + +blockquote code { + background-color: var(--color-blockquote-highlight); +} \ No newline at end of file diff --git a/docs/static/syntax.css b/docs/static/syntax.css index 2e0b2e9def..8e239fd5dd 100644 --- a/docs/static/syntax.css +++ b/docs/static/syntax.css @@ -60,7 +60,6 @@ overflow: auto; display: block; padding: 5px 10px; - margin-bottom: 1em; font-family: var(--font-code); color: var(--color-code-text); background-color: var(--color-code-background); From 94b95d976b490a2342f6f9a18f7b7b36f0e996e7 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Mon, 5 Nov 2018 16:57:17 -0800 Subject: [PATCH 03/10] Some CSS fixes --- docs/static/main.css | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/static/main.css b/docs/static/main.css index 0a57099ad9..84e785a020 100644 --- a/docs/static/main.css +++ b/docs/static/main.css @@ -102,7 +102,7 @@ a:hover { main { flex: 1; - padding: 20px; + padding: 0 20px 20px; color: var(--color-text); } @@ -112,6 +112,10 @@ main { margin: auto; } +main .content { + margin-top: 40px; +} + header { grid-area: header; background: var(--color-heading-background); @@ -183,7 +187,7 @@ nav { } .menu a:hover { - border-bottom: none; + text-decoration: none; } .menu-item { @@ -248,7 +252,7 @@ ul.menu a:hover { } .logo:hover { - border-bottom: none; + text-decoration: none; } code { @@ -271,7 +275,7 @@ strong { } .anchor-link:hover { - border-bottom: none; + text-decoration: none; } .anchor-icon { From 1dfaf637be0b4f7ec2d4063f383e8288c5994c25 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Mon, 5 Nov 2018 17:24:34 -0800 Subject: [PATCH 04/10] Minor updates to getting started from feedback --- docs/content/getting-started.md | 35 ++++++++++++--------------------- docs/static/main.css | 4 ++++ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index 47414b2ee1..a9dfc28d5d 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -24,10 +24,15 @@ Assuming you already have a working [Go environment](https://golang.org/doc/inst $ mkdir -p $GOPATH/src/github.com/[username]/gqlgen-todos ``` +> Go Modules +> +> Currently `gqlgen` does not support Go Modules. This is due to the [`loader`](https://godoc.org/golang.org/x/tools/go/loader) package, that also does not yet support Go Modules. We are looking at solutions to this and the issue is tracked in Github. + Add the following file to your project under `scripts/gqlgen.go`: ```go // +build ignore + package main import "github.com/99designs/gqlgen/cmd" @@ -37,16 +42,12 @@ func main() { } ``` -Lastly, install dependencies: +Lastly, initialise dep. This will inspect any imports you have in your project, and pull down the latest tagged release. ```sh -$ dep ensure +$ dep init ``` -> Go Modules -> -> Currently `gqlgen` does not support Go Modules. This is due to the [`loader`](https://godoc.org/golang.org/x/tools/go/loader) package, that also does not yet support Go Modules. We are looking at solutions to this and the issue is tracked in Github. - ## Building the server ### Define the schema @@ -93,6 +94,7 @@ This has created an empty skeleton with all files you need: - `generated.go` — The GraphQL execution runtime, the bulk of the generated code. - `models_gen.go` — Generated models required to build the graph. Often you will override these with your own models. Still very useful for input types. - `resolver.go` — This is where your application code lives. `generated.go` will call into this to get the data the user has requested. + - `server/server.go` — This is a minimal entry point that sets up an `http.Handler` to the generated GraphQL server. ### Create the database models @@ -125,7 +127,10 @@ Unable to bind Todo.user to github.com/vektah/gqlgen-tutorials/gettingstarted.To no field named user Adding resolver method ``` -*note* we've used the verbose flag here to show what gqlgen is doing. Its looked at all the fields on our model and found matching methods for all of them, except user. For user it added a resolver to the interface we need to implement. This is the magic that makes gqlgen work so well. + +> Note +> +> The verbose flag `-v` is here to show what gqlgen is doing. It has looked at all the fields on the model and found matching methods for all of them, except user. For user it has added a resolver to the interface you need to implement. *This is the magic that makes gqlgen work so well!* ### Implement the resolvers @@ -184,7 +189,7 @@ Now we just need to fill in the `not implemented` parts `graph/graph.go` ```go -//go:generate gorunpkg github.com/99designs/gqlgen +//go:generate go run ./scripts/gqlgen.go package gettingstarted @@ -261,17 +266,3 @@ query findTodos { } } ``` - -## Finishing touches - -At the top of our resolvers.go a go generate command was added that looks like this: -```go -//go:generate go run ./scripts/gqlgen.go -``` - -This magic comment tells `go generate` what command to run when we want to regenerate our code. to do so run: -```go -go generate ./... -``` - -You can now run this command every time you want to regenerate your GraphQL server. diff --git a/docs/static/main.css b/docs/static/main.css index 84e785a020..782bf8d8d5 100644 --- a/docs/static/main.css +++ b/docs/static/main.css @@ -270,6 +270,10 @@ strong { font-weight: 700; } +em { + font-style: italic; +} + .anchor-link { display: inline-block; } From 9dba96d51a60114b2a33a2c296dde02f2306fe41 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Tue, 6 Nov 2018 00:54:13 -0800 Subject: [PATCH 05/10] Fix GraphQL capitalisation --- docs/content/getting-started.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index a9dfc28d5d..750f2edef8 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -1,7 +1,7 @@ --- linkTitle: Getting Started -title: Building graphql servers in golang -description: Get started building type-safe graphql servers in Golang using gqlgen +title: Building GraphQL servers in golang +description: Get started building type-safe GraphQL servers in Golang using gqlgen menu: main weight: -5 --- From 3a7a506259cce1a7da4ea9ebe74e1358d3c7bed0 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Tue, 6 Nov 2018 00:54:29 -0800 Subject: [PATCH 06/10] Add handler import to root cmd --- cmd/root.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/root.go b/cmd/root.go index a680357212..519c2e1af5 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -9,6 +9,9 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/internal/gopath" "github.com/urfave/cli" + + // Required since otherwise dep will prune away these unused packages before codegen has a chance to run + _ "github.com/99designs/gqlgen/handler" ) func Execute() { From 04a72430f5550aa0a052aad20878ccf401eb3b23 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Tue, 6 Nov 2018 01:07:00 -0800 Subject: [PATCH 07/10] Re-add final touches section to getting started --- docs/content/getting-started.md | 38 +++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index 750f2edef8..f4a5769a08 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -95,12 +95,17 @@ This has created an empty skeleton with all files you need: - `models_gen.go` — Generated models required to build the graph. Often you will override these with your own models. Still very useful for input types. - `resolver.go` — This is where your application code lives. `generated.go` will call into this to get the data the user has requested. - `server/server.go` — This is a minimal entry point that sets up an `http.Handler` to the generated GraphQL server. + + Now run dep ensure, so that we can ensure that the newly generated code's dependencies are all present: + + ```sh + $ dep ensure + ``` ### Create the database models -The generated model for Todo isnt quite right, it has a user embeded in it but we only want to fetch it if the user actually requested it. So lets make our own. +The generated model for Todo isn't right, it has a user embeded in it but we only want to fetch it if the user actually requested it. So instead lets make a new model in `todo.go`: -`todo.go` ```go package gettingstarted @@ -112,14 +117,16 @@ type Todo struct { } ``` -And then tell gqlgen to use this new struct by adding this to the `gqlgen.yml`: +Next tell gqlgen to use this new struct by adding it to `gqlgen.yml`: + ```yaml models: Todo: model: github.com/vektah/gqlgen-tutorials/gettingstarted.Todo ``` -and regenerate by running +Regenerate by running: + ```bash $ go run scripts/gqlgen.go -v Unable to bind Todo.user to github.com/vektah/gqlgen-tutorials/gettingstarted.Todo @@ -179,15 +186,14 @@ better than generating. ### Write the resolvers This is a work in progress, we have a way to generate resolver stubs, but it cannot currently update existing code. We can force it to run again by deleting `resolvers.go` and re-running gqlgen: + ```bash -rm resolvers.go -gqlgen +$ rm resolvers.go +$ go run scripts/gqlgen.go ``` -Now we just need to fill in the `not implemented` parts - +Now we just need to fill in the `not implemented` parts. Update `graph/graph.go` -`graph/graph.go` ```go //go:generate go run ./scripts/gqlgen.go @@ -266,3 +272,17 @@ query findTodos { } } ``` + +## Finishing touches + +At the top of our `resolvers.go` add the following line: + +```go +//go:generate go run scripts/gqlgen.go -v +``` + +This magic comment tells `go generate` what command to run when we want to regenerate our code. You can now run any generation commands for your project with: + +```go +go generate ./... +``` From e236d8f36d8b09e034928761523a21dead7014d5 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Tue, 6 Nov 2018 01:13:59 -0800 Subject: [PATCH 08/10] Remove generate command from resolver.go --- codegen/templates/data.go | 2 +- codegen/templates/resolver.gotpl | 2 -- codegen/testserver/resolver.go | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/codegen/templates/data.go b/codegen/templates/data.go index 9b45ed772a..10342d7d69 100644 --- a/codegen/templates/data.go +++ b/codegen/templates/data.go @@ -8,6 +8,6 @@ var data = map[string]string{ "interface.gotpl": "{{- $interface := . }}\n\nfunc (ec *executionContext) _{{$interface.GQLType}}(ctx context.Context, sel ast.SelectionSet, obj *{{$interface.FullName}}) graphql.Marshaler {\n\tswitch obj := (*obj).(type) {\n\tcase nil:\n\t\treturn graphql.Null\n\t{{- range $implementor := $interface.Implementors }}\n\t\t{{- if $implementor.ValueReceiver }}\n\t\t\tcase {{$implementor.FullName}}:\n\t\t\t\treturn ec._{{$implementor.GQLType}}(ctx, sel, &obj)\n\t\t{{- end}}\n\t\tcase *{{$implementor.FullName}}:\n\t\t\treturn ec._{{$implementor.GQLType}}(ctx, sel, obj)\n\t{{- end }}\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unexpected type %T\", obj))\n\t}\n}\n", "models.gotpl": "// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\npackage {{ .PackageName }}\n\nimport (\n\t%%%IMPORTS%%%\n\n\t{{ reserveImport \"context\" }}\n\t{{ reserveImport \"fmt\" }}\n\t{{ reserveImport \"io\" }}\n\t{{ reserveImport \"strconv\" }}\n\t{{ reserveImport \"time\" }}\n\t{{ reserveImport \"sync\" }}\n\t{{ reserveImport \"errors\" }}\n\t{{ reserveImport \"bytes\" }}\n\n\t{{ reserveImport \"github.com/vektah/gqlparser\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser/ast\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql/introspection\" }}\n)\n\n{{ range $model := .Models }}\n\t{{with .Description}} {{.|prefixLines \"// \"}} {{end}}\n\t{{- if .IsInterface }}\n\t\ttype {{.GoType}} interface {\n\t\t\tIs{{.GoType}}()\n\t\t}\n\t{{- else }}\n\t\ttype {{.GoType}} struct {\n\t\t\t{{- range $field := .Fields }}\n\t\t\t\t{{- with .Description}}\n\t\t\t\t\t{{.|prefixLines \"// \"}}\n\t\t\t\t{{- end}}\n\t\t\t\t{{- if $field.GoFieldName }}\n\t\t\t\t\t{{ $field.GoFieldName }} {{$field.Signature}} `json:\"{{$field.GQLName}}\"`\n\t\t\t\t{{- else }}\n\t\t\t\t\t{{ $field.GoFKName }} {{$field.GoFKType}}\n\t\t\t\t{{- end }}\n\t\t\t{{- end }}\n\t\t}\n\n\t\t{{- range $iface := .Implements }}\n\t\t\tfunc ({{$model.GoType}}) Is{{$iface.GoType}}() {}\n\t\t{{- end }}\n\n\t{{- end }}\n{{- end}}\n\n{{ range $enum := .Enums }}\n\t{{with .Description}}{{.|prefixLines \"// \"}} {{end}}\n\ttype {{.GoType}} string\n\tconst (\n\t{{- range $value := .Values}}\n\t\t{{- with .Description}}\n\t\t\t{{.|prefixLines \"// \"}}\n\t\t{{- end}}\n\t\t{{$enum.GoType}}{{ .Name|toCamel }} {{$enum.GoType}} = {{.Name|quote}}\n\t{{- end }}\n\t)\n\n\tfunc (e {{.GoType}}) IsValid() bool {\n\t\tswitch e {\n\t\tcase {{ range $index, $element := .Values}}{{if $index}},{{end}}{{ $enum.GoType }}{{ $element.Name|toCamel }}{{end}}:\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\tfunc (e {{.GoType}}) String() string {\n\t\treturn string(e)\n\t}\n\n\tfunc (e *{{.GoType}}) UnmarshalGQL(v interface{}) error {\n\t\tstr, ok := v.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"enums must be strings\")\n\t\t}\n\n\t\t*e = {{.GoType}}(str)\n\t\tif !e.IsValid() {\n\t\t\treturn fmt.Errorf(\"%s is not a valid {{.GQLType}}\", str)\n\t\t}\n\t\treturn nil\n\t}\n\n\tfunc (e {{.GoType}}) MarshalGQL(w io.Writer) {\n\t\tfmt.Fprint(w, strconv.Quote(e.String()))\n\t}\n\n{{- end }}\n", "object.gotpl": "{{ $object := . }}\n\nvar {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}}\n\n// nolint: gocyclo, errcheck, gas, goconst\n{{- if .Stream }}\nfunc (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.SelectionSet) func() graphql.Marshaler {\n\tfields := graphql.CollectFields(ctx, sel, {{$object.GQLType|lcFirst}}Implementors)\n\tctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\tObject: {{$object.GQLType|quote}},\n\t})\n\tif len(fields) != 1 {\n\t\tec.Errorf(ctx, \"must subscribe to exactly one stream\")\n\t\treturn nil\n\t}\n\n\tswitch fields[0].Name {\n\t{{- range $field := $object.Fields }}\n\tcase \"{{$field.GQLName}}\":\n\t\treturn ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, fields[0])\n\t{{- end }}\n\tdefault:\n\t\tpanic(\"unknown field \" + strconv.Quote(fields[0].Name))\n\t}\n}\n{{- else }}\nfunc (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.SelectionSet{{if not $object.Root}}, obj *{{$object.FullName}} {{end}}) graphql.Marshaler {\n\tfields := graphql.CollectFields(ctx, sel, {{$object.GQLType|lcFirst}}Implementors)\n\t{{if $object.Root}}\n\t\tctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\t\tObject: {{$object.GQLType|quote}},\n\t\t})\n\t{{end}}\n\n\t{{if $object.IsConcurrent}} var wg sync.WaitGroup {{end}}\n\tout := graphql.NewOrderedMap(len(fields))\n\tinvalid := false\n\tfor i, field := range fields {\n\t\tout.Keys[i] = field.Alias\n\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString({{$object.GQLType|quote}})\n\t\t{{- range $field := $object.Fields }}\n\t\tcase \"{{$field.GQLName}}\":\n\t\t\t{{- if $field.IsConcurrent }}\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func(i int, field graphql.CollectedField) {\n\t\t\t{{- end }}\n\t\t\t\tout.Values[i] = ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, field{{if not $object.Root}}, obj{{end}})\n\t\t\t\t{{- if $field.ASTType.NonNull }}\n\t\t\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\t\t\tinvalid = true\n\t\t\t\t\t}\n\t\t\t\t{{- end }}\n\t\t\t{{- if $field.IsConcurrent }}\n\t\t\t\t\twg.Done()\n\t\t\t\t}(i, field)\n\t\t\t{{- end }}\n\t\t{{- end }}\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\t{{if $object.IsConcurrent}} wg.Wait() {{end}}\n\tif invalid { return graphql.Null }\n\treturn out\n}\n{{- end }}\n", - "resolver.gotpl": "//go:generate gorunpkg github.com/99designs/gqlgen\n\npackage {{ .PackageName }}\n\nimport (\n\t%%%IMPORTS%%%\n\n\t{{ reserveImport \"context\" }}\n\t{{ reserveImport \"fmt\" }}\n\t{{ reserveImport \"io\" }}\n\t{{ reserveImport \"strconv\" }}\n\t{{ reserveImport \"time\" }}\n\t{{ reserveImport \"sync\" }}\n\t{{ reserveImport \"errors\" }}\n\t{{ reserveImport \"bytes\" }}\n\n\t{{ reserveImport \"github.com/99designs/gqlgen/handler\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser/ast\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql/introspection\" }}\n)\n\ntype {{.ResolverType}} struct {}\n\n{{ range $object := .Objects -}}\n\t{{- if $object.HasResolvers -}}\n\t\tfunc (r *{{$.ResolverType}}) {{$object.GQLType}}() {{ $object.ResolverInterface.FullName }} {\n\t\t\treturn &{{lcFirst $object.GQLType}}Resolver{r}\n\t\t}\n\t{{ end -}}\n{{ end }}\n\n{{ range $object := .Objects -}}\n\t{{- if $object.HasResolvers -}}\n\t\ttype {{lcFirst $object.GQLType}}Resolver struct { *Resolver }\n\n\t\t{{ range $field := $object.Fields -}}\n\t\t\t{{- if $field.IsResolver -}}\n\t\t\tfunc (r *{{lcFirst $object.GQLType}}Resolver) {{ $field.ShortResolverDeclaration }} {\n\t\t\t\tpanic(\"not implemented\")\n\t\t\t}\n\t\t\t{{ end -}}\n\t\t{{ end -}}\n\t{{ end -}}\n{{ end }}\n", + "resolver.gotpl": "package {{ .PackageName }}\n\nimport (\n\t%%%IMPORTS%%%\n\n\t{{ reserveImport \"context\" }}\n\t{{ reserveImport \"fmt\" }}\n\t{{ reserveImport \"io\" }}\n\t{{ reserveImport \"strconv\" }}\n\t{{ reserveImport \"time\" }}\n\t{{ reserveImport \"sync\" }}\n\t{{ reserveImport \"errors\" }}\n\t{{ reserveImport \"bytes\" }}\n\n\t{{ reserveImport \"github.com/99designs/gqlgen/handler\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser/ast\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql/introspection\" }}\n)\n\ntype {{.ResolverType}} struct {}\n\n{{ range $object := .Objects -}}\n\t{{- if $object.HasResolvers -}}\n\t\tfunc (r *{{$.ResolverType}}) {{$object.GQLType}}() {{ $object.ResolverInterface.FullName }} {\n\t\t\treturn &{{lcFirst $object.GQLType}}Resolver{r}\n\t\t}\n\t{{ end -}}\n{{ end }}\n\n{{ range $object := .Objects -}}\n\t{{- if $object.HasResolvers -}}\n\t\ttype {{lcFirst $object.GQLType}}Resolver struct { *Resolver }\n\n\t\t{{ range $field := $object.Fields -}}\n\t\t\t{{- if $field.IsResolver -}}\n\t\t\tfunc (r *{{lcFirst $object.GQLType}}Resolver) {{ $field.ShortResolverDeclaration }} {\n\t\t\t\tpanic(\"not implemented\")\n\t\t\t}\n\t\t\t{{ end -}}\n\t\t{{ end -}}\n\t{{ end -}}\n{{ end }}\n", "server.gotpl": "package main\n\nimport (\n\t%%%IMPORTS%%%\n\n\t{{ reserveImport \"context\" }}\n\t{{ reserveImport \"log\" }}\n\t{{ reserveImport \"net/http\" }}\n\t{{ reserveImport \"os\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/handler\" }}\n)\n\nconst defaultPort = \"8080\"\n\nfunc main() {\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = defaultPort\n\t}\n\n\thttp.Handle(\"/\", handler.Playground(\"GraphQL playground\", \"/query\"))\n\thttp.Handle(\"/query\", handler.GraphQL({{ lookupImport .ExecPackageName }}.NewExecutableSchema({{ lookupImport .ExecPackageName}}.Config{Resolvers: &{{ lookupImport .ResolverPackageName}}.Resolver{}})))\n\n\tlog.Printf(\"connect to http://localhost:%s/ for GraphQL playground\", port)\n\tlog.Fatal(http.ListenAndServe(\":\" + port, nil))\n}\n", } diff --git a/codegen/templates/resolver.gotpl b/codegen/templates/resolver.gotpl index 8628506c86..53ba8c4338 100644 --- a/codegen/templates/resolver.gotpl +++ b/codegen/templates/resolver.gotpl @@ -1,5 +1,3 @@ -//go:generate gorunpkg github.com/99designs/gqlgen - package {{ .PackageName }} import ( diff --git a/codegen/testserver/resolver.go b/codegen/testserver/resolver.go index a5ef197d70..f7d04e16cb 100644 --- a/codegen/testserver/resolver.go +++ b/codegen/testserver/resolver.go @@ -1,5 +1,3 @@ -//go:generate gorunpkg github.com/99designs/gqlgen - package testserver import ( From b9fbb642f1f90bf8b173cfabdfc88d24ec36344d Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Tue, 13 Nov 2018 10:51:36 +1100 Subject: [PATCH 09/10] Mention recursive-ness of generate ./... --- docs/content/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index f4a5769a08..a3a84d4b3c 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -281,7 +281,7 @@ At the top of our `resolvers.go` add the following line: //go:generate go run scripts/gqlgen.go -v ``` -This magic comment tells `go generate` what command to run when we want to regenerate our code. You can now run any generation commands for your project with: +This magic comment tells `go generate` what command to run when we want to regenerate our code. To run go generate recursively over your entire project, use this command: ```go go generate ./... From 878f3945f1ec84aa26400f67a714fd8cc7db40e3 Mon Sep 17 00:00:00 2001 From: Mathew Byrne Date: Wed, 28 Nov 2018 13:21:12 +1100 Subject: [PATCH 10/10] Minor fixes to getting started code examples --- docs/content/getting-started.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index a3a84d4b3c..285a84478d 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -122,14 +122,14 @@ Next tell gqlgen to use this new struct by adding it to `gqlgen.yml`: ```yaml models: Todo: - model: github.com/vektah/gqlgen-tutorials/gettingstarted.Todo + model: github.com/[username]/gqlgen-todos/gettingstarted.Todo ``` Regenerate by running: ```bash $ go run scripts/gqlgen.go -v -Unable to bind Todo.user to github.com/vektah/gqlgen-tutorials/gettingstarted.Todo +Unable to bind Todo.user to github.com/[username]/gqlgen-todos/gettingstarted.Todo no method named user no field named user Adding resolver method @@ -185,10 +185,10 @@ better than generating. ### Write the resolvers -This is a work in progress, we have a way to generate resolver stubs, but it cannot currently update existing code. We can force it to run again by deleting `resolvers.go` and re-running gqlgen: +This is a work in progress, we have a way to generate resolver stubs, but it cannot currently update existing code. We can force it to run again by deleting `resolver.go` and re-running gqlgen: ```bash -$ rm resolvers.go +$ rm resolver.go $ go run scripts/gqlgen.go ``` @@ -275,7 +275,7 @@ query findTodos { ## Finishing touches -At the top of our `resolvers.go` add the following line: +At the top of our `resolver.go` add the following line: ```go //go:generate go run scripts/gqlgen.go -v