From be1647480a2c89bac80c094e0fa7f5ffda7fe602 Mon Sep 17 00:00:00 2001 From: Michael Tibben Date: Wed, 9 Feb 2022 13:00:36 +1100 Subject: [PATCH] Cleanup main --- ambient.go | 19 +++ cmd/ambient.go | 10 -- cmd/gen.go | 43 ------- cmd/init.go | 198 ----------------------------- cmd/init_test.go | 42 ------- cmd/root.go | 45 ------- cmd/testdata/init/.keep | 0 cmd/version.go | 17 --- codegen/generate.go | 3 +- main.go | 267 +++++++++++++++++++++++++++++++++++++++- 10 files changed, 285 insertions(+), 359 deletions(-) create mode 100644 ambient.go delete mode 100644 cmd/ambient.go delete mode 100644 cmd/gen.go delete mode 100644 cmd/init.go delete mode 100644 cmd/init_test.go delete mode 100644 cmd/root.go delete mode 100644 cmd/testdata/init/.keep delete mode 100644 cmd/version.go diff --git a/ambient.go b/ambient.go new file mode 100644 index 00000000000..edc9365857d --- /dev/null +++ b/ambient.go @@ -0,0 +1,19 @@ +package main + +// Ambient imports +// +// These imports are referenced by the generated code. +// Dependency managers like dep have been known to prune them, +// so explicitly import them here. +// +// TODO: check if these imports are still necessary with go +// modules, and if so move the ambient import to the files +// where they are actually referenced +import ( + _ "github.com/99designs/gqlgen/graphql" + _ "github.com/99designs/gqlgen/graphql/handler" + _ "github.com/99designs/gqlgen/graphql/introspection" + _ "github.com/99designs/gqlgen/handler" + _ "github.com/vektah/gqlparser/v2" + _ "github.com/vektah/gqlparser/v2/ast" +) diff --git a/cmd/ambient.go b/cmd/ambient.go deleted file mode 100644 index 0f3655d34fa..00000000000 --- a/cmd/ambient.go +++ /dev/null @@ -1,10 +0,0 @@ -package cmd - -import ( - // Import and ignore the ambient imports listed below so dependency managers - // don't prune unused code for us. Both lists should be kept in sync. - _ "github.com/99designs/gqlgen/graphql" - _ "github.com/99designs/gqlgen/graphql/introspection" - _ "github.com/vektah/gqlparser/v2" - _ "github.com/vektah/gqlparser/v2/ast" -) diff --git a/cmd/gen.go b/cmd/gen.go deleted file mode 100644 index f973cc22c0f..00000000000 --- a/cmd/gen.go +++ /dev/null @@ -1,43 +0,0 @@ -package cmd - -import ( - "errors" - "io/fs" - - "github.com/99designs/gqlgen/api" - "github.com/99designs/gqlgen/codegen/config" - "github.com/urfave/cli/v2" -) - -var genCmd = &cli.Command{ - Name: "generate", - Usage: "generate a graphql server based on schema", - Flags: []cli.Flag{ - &cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, - &cli.StringFlag{Name: "config, c", Usage: "the config filename"}, - }, - Action: func(ctx *cli.Context) error { - var cfg *config.Config - var err error - if configFilename := ctx.String("config"); configFilename != "" { - cfg, err = config.LoadConfig(configFilename) - if err != nil { - return err - } - } else { - cfg, err = config.LoadConfigFromDefaultLocations() - if errors.Is(err, fs.ErrNotExist) { - cfg, err = config.LoadDefaultConfig() - } - - if err != nil { - return err - } - } - - if err = api.Generate(cfg); err != nil { - return err - } - return nil - }, -} diff --git a/cmd/init.go b/cmd/init.go deleted file mode 100644 index a5526e1966d..00000000000 --- a/cmd/init.go +++ /dev/null @@ -1,198 +0,0 @@ -package cmd - -import ( - "bytes" - "errors" - "fmt" - "html/template" - "io/fs" - "io/ioutil" - "os" - "path/filepath" - - "github.com/99designs/gqlgen/api" - "github.com/99designs/gqlgen/codegen/config" - "github.com/99designs/gqlgen/internal/code" - "github.com/99designs/gqlgen/plugin/servergen" - "github.com/urfave/cli/v2" -) - -var configTemplate = template.Must(template.New("name").Parse( - `# Where are all the schema files located? globs are supported eg src/**/*.graphqls -schema: - - graph/*.graphqls - -# Where should the generated server code go? -exec: - filename: graph/generated/generated.go - package: generated - -# Uncomment to enable federation -# federation: -# filename: graph/generated/federation.go -# package: generated - -# Where should any generated models go? -model: - filename: graph/model/models_gen.go - package: model - -# Where should the resolver implementations go? -resolver: - layout: follow-schema - dir: graph - package: graph - -# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models -# struct_tag: json - -# Optional: turn on to use []Thing instead of []*Thing -# omit_slice_element_pointers: false - -# Optional: set to speed up generation time by not performing a final validation pass. -# skip_validation: true - -# gqlgen will search for any type names in the schema in these go packages -# if they match it will use them, otherwise it will generate them. -autobind: -# - "{{.}}/graph/model" - -# This section declares type mapping between the GraphQL and go type systems -# -# The first line in each type will be used as defaults for resolver arguments and -# modelgen, the others will be allowed when binding to fields. Configure them to -# your liking -models: - ID: - model: - - github.com/99designs/gqlgen/graphql.ID - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 - Int: - model: - - github.com/99designs/gqlgen/graphql.Int - - github.com/99designs/gqlgen/graphql.Int64 - - github.com/99designs/gqlgen/graphql.Int32 -`)) - -var schemaDefault = `# GraphQL schema example -# -# https://gqlgen.com/getting-started/ - -type Todo { - id: ID! - text: String! - done: Boolean! - user: User! -} - -type User { - id: ID! - name: String! -} - -type Query { - todos: [Todo!]! -} - -input NewTodo { - text: String! - userId: String! -} - -type Mutation { - createTodo(input: NewTodo!): Todo! -} -` - -var initCmd = &cli.Command{ - Name: "init", - Usage: "create a new gqlgen project", - Flags: []cli.Flag{ - &cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, - &cli.StringFlag{Name: "config, c", Usage: "the config filename", Value: "gqlgen.yml"}, - &cli.StringFlag{Name: "server", Usage: "where to write the server stub to", Value: "server.go"}, - &cli.StringFlag{Name: "schema", Usage: "where to write the schema stub to", Value: "graph/schema.graphqls"}, - }, - Action: func(ctx *cli.Context) error { - configFilename := ctx.String("config") - serverFilename := ctx.String("server") - schemaFilename := ctx.String("schema") - - pkgName := code.ImportPathForDir(".") - if pkgName == "" { - return fmt.Errorf("unable to determine import path for current directory, you probably need to run go mod init first") - } - - // check schema and config don't already exist - for _, filename := range []string{configFilename, schemaFilename, serverFilename} { - if fileExists(filename) { - return fmt.Errorf("%s already exists", filename) - } - } - _, err := config.LoadConfigFromDefaultLocations() - if err == nil { - return fmt.Errorf("gqlgen.yml already exists in a parent directory\n") - } - - // create config - fmt.Println("Creating", configFilename) - if err := initFile(configFilename, executeConfigTemplate(pkgName)); err != nil { - return err - } - - // create schema - fmt.Println("Creating", schemaFilename) - if err := initFile(schemaFilename, schemaDefault); err != nil { - return err - } - - // create the package directory with a temporary file so that go recognises it as a package - // and autobinding doesn't error out - tmpPackageNameFile := "graph/model/_tmp_gqlgen_init.go" - if err := initFile(tmpPackageNameFile, "package model"); err != nil { - return err - } - defer os.Remove(tmpPackageNameFile) - - var cfg *config.Config - if cfg, err = config.LoadConfig(configFilename); err != nil { - panic(err) - } - - fmt.Println("Creating", serverFilename) - fmt.Println("Generating...") - if err := api.Generate(cfg, api.AddPlugin(servergen.New(serverFilename))); err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } - - fmt.Printf("\nExec \"go run ./%s\" to start GraphQL server\n", serverFilename) - return nil - }, -} - -func executeConfigTemplate(pkgName string) string { - var buf bytes.Buffer - if err := configTemplate.Execute(&buf, pkgName); err != nil { - panic(err) - } - - return buf.String() -} - -func fileExists(filename string) bool { - _, err := os.Stat(filename) - return !errors.Is(err, fs.ErrNotExist) -} - -func initFile(filename, contents string) error { - if err := os.MkdirAll(filepath.Dir(filename), 0o755); err != nil { - return fmt.Errorf("unable to create directory for file '%s': %w\n", filename, err) - } - if err := ioutil.WriteFile(filename, []byte(contents), 0o644); err != nil { - return fmt.Errorf("unable to write file '%s': %w\n", filename, err) - } - - return nil -} diff --git a/cmd/init_test.go b/cmd/init_test.go deleted file mode 100644 index df14d495b8d..00000000000 --- a/cmd/init_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package cmd - -import ( - "os" - "path" - "testing" - - "github.com/stretchr/testify/require" - "github.com/urfave/cli/v2" -) - -func cleanupGenerate() { - // remove generated files - _ = os.Remove("gqlgen.yml") - _ = os.Remove("server.go") - _ = os.RemoveAll("graph") -} - -func TestInitCmd(t *testing.T) { - // setup test dir - wd, _ := os.Getwd() - testpath := path.Join(wd, "testdata", "init") - defer func() { - // remove generated files - cleanupGenerate() - _ = os.Chdir(wd) - }() - _ = os.Chdir(testpath) - - // Should ok if dir is empty - app := cli.NewApp() - app.Commands = []*cli.Command{initCmd} - args := os.Args[0:1] - args = append(args, "init") - err := app.Run(args) - require.Nil(t, err) - - // Should fail if dir is not empty, e.g. gqlgen.yml exists - err = app.Run(args) - require.NotNil(t, err) - require.Equal(t, "gqlgen.yml already exists", err.Error()) -} diff --git a/cmd/root.go b/cmd/root.go deleted file mode 100644 index ce96c1b86d2..00000000000 --- a/cmd/root.go +++ /dev/null @@ -1,45 +0,0 @@ -package cmd - -import ( - "fmt" - "io/ioutil" - "log" - "os" - - "github.com/99designs/gqlgen/graphql" - "github.com/urfave/cli/v2" - - // Required since otherwise dep will prune away these unused packages before codegen has a chance to run - _ "github.com/99designs/gqlgen/graphql/handler" - _ "github.com/99designs/gqlgen/handler" -) - -func Execute() { - app := cli.NewApp() - app.Name = "gqlgen" - app.Usage = genCmd.Usage - app.Description = "This is a library for quickly creating strictly typed graphql servers in golang. See https://gqlgen.com/ for a getting started guide." - app.HideVersion = true - app.Flags = genCmd.Flags - app.Version = graphql.Version - app.Before = func(context *cli.Context) error { - if context.Bool("verbose") { - log.SetFlags(0) - } else { - log.SetOutput(ioutil.Discard) - } - return nil - } - - app.Action = genCmd.Action - app.Commands = []*cli.Command{ - genCmd, - initCmd, - versionCmd, - } - - if err := app.Run(os.Args); err != nil { - fmt.Fprint(os.Stderr, err.Error()+"\n") - os.Exit(1) - } -} diff --git a/cmd/testdata/init/.keep b/cmd/testdata/init/.keep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/cmd/version.go b/cmd/version.go deleted file mode 100644 index d3a05deda28..00000000000 --- a/cmd/version.go +++ /dev/null @@ -1,17 +0,0 @@ -package cmd - -import ( - "fmt" - - "github.com/99designs/gqlgen/graphql" - "github.com/urfave/cli/v2" -) - -var versionCmd = &cli.Command{ - Name: "version", - Usage: "print the version string", - Action: func(ctx *cli.Context) error { - fmt.Println(graphql.Version) - return nil - }, -} diff --git a/codegen/generate.go b/codegen/generate.go index cd558fd2a93..77bbf575111 100644 --- a/codegen/generate.go +++ b/codegen/generate.go @@ -8,10 +8,9 @@ import ( "runtime" "strings" - "github.com/vektah/gqlparser/v2/ast" - "github.com/99designs/gqlgen/codegen/config" "github.com/99designs/gqlgen/codegen/templates" + "github.com/vektah/gqlparser/v2/ast" ) func GenerateCode(data *Data) error { diff --git a/main.go b/main.go index dbc24135388..1450138d061 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,272 @@ package main import ( - "github.com/99designs/gqlgen/cmd" + "bytes" + "errors" + "fmt" + "html/template" + "io/fs" + "io/ioutil" + "log" + "os" + "path/filepath" + + "github.com/99designs/gqlgen/api" + "github.com/99designs/gqlgen/codegen/config" + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/internal/code" + "github.com/99designs/gqlgen/plugin/servergen" + "github.com/urfave/cli/v2" ) +var configTemplate = template.Must(template.New("name").Parse( + `# Where are all the schema files located? globs are supported eg src/**/*.graphqls +schema: + - graph/*.graphqls + +# Where should the generated server code go? +exec: + filename: graph/generated/generated.go + package: generated + +# Uncomment to enable federation +# federation: +# filename: graph/generated/federation.go +# package: generated + +# Where should any generated models go? +model: + filename: graph/model/models_gen.go + package: model + +# Where should the resolver implementations go? +resolver: + layout: follow-schema + dir: graph + package: graph + +# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models +# struct_tag: json + +# Optional: turn on to use []Thing instead of []*Thing +# omit_slice_element_pointers: false + +# Optional: set to speed up generation time by not performing a final validation pass. +# skip_validation: true + +# gqlgen will search for any type names in the schema in these go packages +# if they match it will use them, otherwise it will generate them. +autobind: +# - "{{.}}/graph/model" + +# This section declares type mapping between the GraphQL and go type systems +# +# The first line in each type will be used as defaults for resolver arguments and +# modelgen, the others will be allowed when binding to fields. Configure them to +# your liking +models: + ID: + model: + - github.com/99designs/gqlgen/graphql.ID + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 + Int: + model: + - github.com/99designs/gqlgen/graphql.Int + - github.com/99designs/gqlgen/graphql.Int64 + - github.com/99designs/gqlgen/graphql.Int32 +`)) + +var schemaDefault = `# GraphQL schema example +# +# https://gqlgen.com/getting-started/ + +type Todo { + id: ID! + text: String! + done: Boolean! + user: User! +} + +type User { + id: ID! + name: String! +} + +type Query { + todos: [Todo!]! +} + +input NewTodo { + text: String! + userId: String! +} + +type Mutation { + createTodo(input: NewTodo!): Todo! +} +` + +func executeConfigTemplate(pkgName string) string { + var buf bytes.Buffer + if err := configTemplate.Execute(&buf, pkgName); err != nil { + panic(err) + } + + return buf.String() +} + +func fileExists(filename string) bool { + _, err := os.Stat(filename) + return !errors.Is(err, fs.ErrNotExist) +} + +func initFile(filename, contents string) error { + if err := os.MkdirAll(filepath.Dir(filename), 0o755); err != nil { + return fmt.Errorf("unable to create directory for file '%s': %w\n", filename, err) + } + if err := ioutil.WriteFile(filename, []byte(contents), 0o644); err != nil { + return fmt.Errorf("unable to write file '%s': %w\n", filename, err) + } + + return nil +} + +var initCmd = &cli.Command{ + Name: "init", + Usage: "create a new gqlgen project", + Flags: []cli.Flag{ + &cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, + &cli.StringFlag{Name: "config, c", Usage: "the config filename", Value: "gqlgen.yml"}, + &cli.StringFlag{Name: "server", Usage: "where to write the server stub to", Value: "server.go"}, + &cli.StringFlag{Name: "schema", Usage: "where to write the schema stub to", Value: "graph/schema.graphqls"}, + }, + Action: func(ctx *cli.Context) error { + configFilename := ctx.String("config") + serverFilename := ctx.String("server") + schemaFilename := ctx.String("schema") + + pkgName := code.ImportPathForDir(".") + if pkgName == "" { + return fmt.Errorf("unable to determine import path for current directory, you probably need to run go mod init first") + } + + // check schema and config don't already exist + for _, filename := range []string{configFilename, schemaFilename, serverFilename} { + if fileExists(filename) { + return fmt.Errorf("%s already exists", filename) + } + } + _, err := config.LoadConfigFromDefaultLocations() + if err == nil { + return fmt.Errorf("gqlgen.yml already exists in a parent directory\n") + } + + // create config + fmt.Println("Creating", configFilename) + if err := initFile(configFilename, executeConfigTemplate(pkgName)); err != nil { + return err + } + + // create schema + fmt.Println("Creating", schemaFilename) + if err := initFile(schemaFilename, schemaDefault); err != nil { + return err + } + + // create the package directory with a temporary file so that go recognises it as a package + // and autobinding doesn't error out + tmpPackageNameFile := "graph/model/_tmp_gqlgen_init.go" + if err := initFile(tmpPackageNameFile, "package model"); err != nil { + return err + } + defer os.Remove(tmpPackageNameFile) + + var cfg *config.Config + if cfg, err = config.LoadConfig(configFilename); err != nil { + panic(err) + } + + fmt.Println("Creating", serverFilename) + fmt.Println("Generating...") + if err := api.Generate(cfg, api.AddPlugin(servergen.New(serverFilename))); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + } + + fmt.Printf("\nExec \"go run ./%s\" to start GraphQL server\n", serverFilename) + return nil + }, +} + +var generateCmd = &cli.Command{ + Name: "generate", + Usage: "generate a graphql server based on schema", + Flags: []cli.Flag{ + &cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, + &cli.StringFlag{Name: "config, c", Usage: "the config filename"}, + }, + Action: func(ctx *cli.Context) error { + var cfg *config.Config + var err error + if configFilename := ctx.String("config"); configFilename != "" { + cfg, err = config.LoadConfig(configFilename) + if err != nil { + return err + } + } else { + cfg, err = config.LoadConfigFromDefaultLocations() + if errors.Is(err, fs.ErrNotExist) { + cfg, err = config.LoadDefaultConfig() + } + + if err != nil { + return err + } + } + + if err = api.Generate(cfg); err != nil { + return err + } + return nil + }, +} + +var versionCmd = &cli.Command{ + Name: "version", + Usage: "print the version string", + Action: func(ctx *cli.Context) error { + fmt.Println(graphql.Version) + return nil + }, +} + func main() { - cmd.Execute() + app := cli.NewApp() + app.Name = "gqlgen" + app.Usage = generateCmd.Usage + app.Description = "This is a library for quickly creating strictly typed graphql servers in golang. See https://gqlgen.com/ for a getting started guide." + app.HideVersion = true + app.Flags = generateCmd.Flags + app.Version = graphql.Version + app.Before = func(context *cli.Context) error { + if context.Bool("verbose") { + log.SetFlags(0) + } else { + log.SetOutput(ioutil.Discard) + } + return nil + } + + app.Action = generateCmd.Action + app.Commands = []*cli.Command{ + generateCmd, + initCmd, + versionCmd, + } + + if err := app.Run(os.Args); err != nil { + fmt.Fprint(os.Stderr, err.Error()+"\n") + os.Exit(1) + } }