github.com/99designs/gqlgen@v0.17.45/main.go (about) 1 package main 2 3 //go:generate sh generate_examples.sh 4 5 import ( 6 "bytes" 7 _ "embed" 8 "errors" 9 "fmt" 10 "html/template" 11 "io" 12 "io/fs" 13 "log" 14 "os" 15 "path/filepath" 16 17 "github.com/urfave/cli/v2" 18 19 "github.com/99designs/gqlgen/api" 20 "github.com/99designs/gqlgen/codegen/config" 21 "github.com/99designs/gqlgen/graphql" 22 "github.com/99designs/gqlgen/internal/code" 23 "github.com/99designs/gqlgen/plugin/servergen" 24 ) 25 26 //go:embed init-templates/schema.graphqls 27 var schemaFileContent string 28 29 //go:embed init-templates/gqlgen.yml.gotmpl 30 var configFileTemplate string 31 32 func getConfigFileContent(pkgName string) string { 33 var buf bytes.Buffer 34 if err := template.Must(template.New("gqlgen.yml").Parse(configFileTemplate)).Execute(&buf, pkgName); err != nil { 35 panic(err) 36 } 37 return buf.String() 38 } 39 40 func fileExists(filename string) bool { 41 _, err := os.Stat(filename) 42 return !errors.Is(err, fs.ErrNotExist) 43 } 44 45 // see Go source code: 46 // https://github.com/golang/go/blob/f57ebed35132d02e5cf016f324853217fb545e91/src/cmd/go/internal/modload/init.go#L1283 47 func findModuleRoot(dir string) (roots string) { 48 if dir == "" { 49 panic("dir not set") 50 } 51 dir = filepath.Clean(dir) 52 53 // Look for enclosing go.mod. 54 for { 55 if fi, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil && !fi.IsDir() { 56 return dir 57 } 58 d := filepath.Dir(dir) 59 if d == dir { // the parent of the root is itself, so we can go no further 60 break 61 } 62 dir = d 63 } 64 return "" 65 } 66 67 func initFile(filename, contents string) error { 68 if err := os.MkdirAll(filepath.Dir(filename), 0o755); err != nil { 69 return fmt.Errorf("unable to create directory for file '%s': %w\n", filename, err) 70 } 71 if err := os.WriteFile(filename, []byte(contents), 0o644); err != nil { 72 return fmt.Errorf("unable to write file '%s': %w\n", filename, err) 73 } 74 75 return nil 76 } 77 78 var initCmd = &cli.Command{ 79 Name: "init", 80 Usage: "create a new gqlgen project", 81 Flags: []cli.Flag{ 82 &cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, 83 &cli.StringFlag{Name: "config, c", Usage: "the config filename", Value: "gqlgen.yml"}, 84 &cli.StringFlag{ 85 Name: "server", 86 Usage: "where to write the server stub to", 87 Value: "server.go", 88 }, 89 &cli.StringFlag{ 90 Name: "schema", 91 Usage: "where to write the schema stub to", 92 Value: "graph/schema.graphqls", 93 }, 94 }, 95 Action: func(ctx *cli.Context) error { 96 configFilename := ctx.String("config") 97 serverFilename := ctx.String("server") 98 schemaFilename := ctx.String("schema") 99 100 cwd, err := os.Getwd() 101 if err != nil { 102 log.Println(err) 103 return fmt.Errorf("unable to determine current directory:%w", err) 104 } 105 pkgName := code.ImportPathForDir(cwd) 106 if pkgName == "" { 107 return fmt.Errorf( 108 "unable to determine import path for current directory, you probably need to run 'go mod init' first", 109 ) 110 } 111 modRoot := findModuleRoot(cwd) 112 if modRoot == "" { 113 return fmt.Errorf("go.mod is missing. Please, do 'go mod init' first\n") 114 } 115 116 // check schema and config don't already exist 117 for _, filename := range []string{configFilename, schemaFilename, serverFilename} { 118 if fileExists(filename) { 119 return fmt.Errorf("%s already exists", filename) 120 } 121 } 122 _, err = config.LoadConfigFromDefaultLocations() 123 if err == nil { 124 return fmt.Errorf("gqlgen.yml already exists in a parent directory\n") 125 } 126 127 // create config 128 fmt.Println("Creating", configFilename) 129 if err := initFile(configFilename, getConfigFileContent(pkgName)); err != nil { 130 return err 131 } 132 133 // create schema 134 fmt.Println("Creating", schemaFilename) 135 136 if err := initFile(schemaFilename, schemaFileContent); err != nil { 137 return err 138 } 139 140 // create the package directory with a temporary file so that go recognises it as a package 141 // and autobinding doesn't error out 142 tmpPackageNameFile := "graph/model/_tmp_gqlgen_init.go" 143 if err := initFile(tmpPackageNameFile, "package model"); err != nil { 144 return err 145 } 146 defer os.Remove(tmpPackageNameFile) 147 148 var cfg *config.Config 149 if cfg, err = config.LoadConfig(configFilename); err != nil { 150 panic(err) 151 } 152 153 fmt.Println("Creating", serverFilename) 154 fmt.Println("Generating...") 155 if err := api.Generate(cfg, api.AddPlugin(servergen.New(serverFilename))); err != nil { 156 return err 157 } 158 159 fmt.Printf("\nExec \"go run ./%s\" to start GraphQL server\n", serverFilename) 160 return nil 161 }, 162 } 163 164 var generateCmd = &cli.Command{ 165 Name: "generate", 166 Usage: "generate a graphql server based on schema", 167 Flags: []cli.Flag{ 168 &cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, 169 &cli.StringFlag{Name: "config, c", Usage: "the config filename"}, 170 }, 171 Action: func(ctx *cli.Context) error { 172 var cfg *config.Config 173 var err error 174 if configFilename := ctx.String("config"); configFilename != "" { 175 cfg, err = config.LoadConfig(configFilename) 176 if err != nil { 177 return err 178 } 179 } else { 180 cfg, err = config.LoadConfigFromDefaultLocations() 181 if errors.Is(err, fs.ErrNotExist) { 182 cfg, err = config.LoadDefaultConfig() 183 } 184 185 if err != nil { 186 return err 187 } 188 } 189 190 if err = api.Generate(cfg); err != nil { 191 return err 192 } 193 return nil 194 }, 195 } 196 197 var versionCmd = &cli.Command{ 198 Name: "version", 199 Usage: "print the version string", 200 Action: func(ctx *cli.Context) error { 201 fmt.Println(graphql.Version) 202 return nil 203 }, 204 } 205 206 func main() { 207 app := cli.NewApp() 208 app.Name = "gqlgen" 209 app.Usage = generateCmd.Usage 210 app.Description = "This is a library for quickly creating strictly typed graphql servers in golang. See https://gqlgen.com/ for a getting started guide." 211 app.HideVersion = true 212 app.Flags = generateCmd.Flags 213 app.Version = graphql.Version 214 app.Before = func(context *cli.Context) error { 215 if context.Bool("verbose") { 216 log.SetFlags(0) 217 } else { 218 log.SetOutput(io.Discard) 219 } 220 return nil 221 } 222 223 app.Action = generateCmd.Action 224 app.Commands = []*cli.Command{ 225 generateCmd, 226 initCmd, 227 versionCmd, 228 } 229 230 if err := app.Run(os.Args); err != nil { 231 fmt.Fprint(os.Stderr, err.Error()+"\n") 232 os.Exit(1) 233 } 234 }