github.com/operandinc/gqlgen@v0.16.1/main.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "html/template" 8 "io/fs" 9 "io/ioutil" 10 "log" 11 "os" 12 "path/filepath" 13 14 "github.com/operandinc/gqlgen/api" 15 "github.com/operandinc/gqlgen/codegen/config" 16 "github.com/operandinc/gqlgen/graphql" 17 "github.com/operandinc/gqlgen/internal/code" 18 "github.com/operandinc/gqlgen/plugin/servergen" 19 "github.com/urfave/cli/v2" 20 ) 21 22 var configTemplate = template.Must(template.New("name").Parse( 23 `# Where are all the schema files located? globs are supported eg src/**/*.graphqls 24 schema: 25 - graph/*.graphqls 26 27 # Where should the generated server code go? 28 exec: 29 filename: graph/generated/generated.go 30 package: generated 31 32 # Uncomment to enable federation 33 # federation: 34 # filename: graph/generated/federation.go 35 # package: generated 36 37 # Where should any generated models go? 38 model: 39 filename: graph/model/models_gen.go 40 package: model 41 42 # Where should the resolver implementations go? 43 resolver: 44 layout: follow-schema 45 dir: graph 46 package: graph 47 48 # Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models 49 # struct_tag: json 50 51 # Optional: turn on to use []Thing instead of []*Thing 52 # omit_slice_element_pointers: false 53 54 # Optional: set to speed up generation time by not performing a final validation pass. 55 # skip_validation: true 56 57 # gqlgen will search for any type names in the schema in these go packages 58 # if they match it will use them, otherwise it will generate them. 59 autobind: 60 # - "{{.}}/graph/model" 61 62 # This section declares type mapping between the GraphQL and go type systems 63 # 64 # The first line in each type will be used as defaults for resolver arguments and 65 # modelgen, the others will be allowed when binding to fields. Configure them to 66 # your liking 67 models: 68 ID: 69 model: 70 - github.com/operandinc/gqlgen/graphql.ID 71 - github.com/operandinc/gqlgen/graphql.Int 72 - github.com/operandinc/gqlgen/graphql.Int64 73 - github.com/operandinc/gqlgen/graphql.Int32 74 Int: 75 model: 76 - github.com/operandinc/gqlgen/graphql.Int 77 - github.com/operandinc/gqlgen/graphql.Int64 78 - github.com/operandinc/gqlgen/graphql.Int32 79 `)) 80 81 var schemaDefault = `# GraphQL schema example 82 # 83 # https://gqlgen.com/getting-started/ 84 85 type Todo { 86 id: ID! 87 text: String! 88 done: Boolean! 89 user: User! 90 } 91 92 type User { 93 id: ID! 94 name: String! 95 } 96 97 type Query { 98 todos: [Todo!]! 99 } 100 101 input NewTodo { 102 text: String! 103 userId: String! 104 } 105 106 type Mutation { 107 createTodo(input: NewTodo!): Todo! 108 } 109 ` 110 111 func executeConfigTemplate(pkgName string) string { 112 var buf bytes.Buffer 113 if err := configTemplate.Execute(&buf, pkgName); err != nil { 114 panic(err) 115 } 116 117 return buf.String() 118 } 119 120 func fileExists(filename string) bool { 121 _, err := os.Stat(filename) 122 return !errors.Is(err, fs.ErrNotExist) 123 } 124 125 func initFile(filename, contents string) error { 126 if err := os.MkdirAll(filepath.Dir(filename), 0o755); err != nil { 127 return fmt.Errorf("unable to create directory for file '%s': %w\n", filename, err) 128 } 129 if err := ioutil.WriteFile(filename, []byte(contents), 0o644); err != nil { 130 return fmt.Errorf("unable to write file '%s': %w\n", filename, err) 131 } 132 133 return nil 134 } 135 136 var initCmd = &cli.Command{ 137 Name: "init", 138 Usage: "create a new gqlgen project", 139 Flags: []cli.Flag{ 140 &cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, 141 &cli.StringFlag{Name: "config, c", Usage: "the config filename", Value: "gqlgen.yml"}, 142 &cli.StringFlag{Name: "server", Usage: "where to write the server stub to", Value: "server.go"}, 143 &cli.StringFlag{Name: "schema", Usage: "where to write the schema stub to", Value: "graph/schema.graphqls"}, 144 }, 145 Action: func(ctx *cli.Context) error { 146 configFilename := ctx.String("config") 147 serverFilename := ctx.String("server") 148 schemaFilename := ctx.String("schema") 149 150 pkgName := code.ImportPathForDir(".") 151 if pkgName == "" { 152 return fmt.Errorf("unable to determine import path for current directory, you probably need to run go mod init first") 153 } 154 155 // check schema and config don't already exist 156 for _, filename := range []string{configFilename, schemaFilename, serverFilename} { 157 if fileExists(filename) { 158 return fmt.Errorf("%s already exists", filename) 159 } 160 } 161 _, err := config.LoadConfigFromDefaultLocations() 162 if err == nil { 163 return fmt.Errorf("gqlgen.yml already exists in a parent directory\n") 164 } 165 166 // create config 167 fmt.Println("Creating", configFilename) 168 if err := initFile(configFilename, executeConfigTemplate(pkgName)); err != nil { 169 return err 170 } 171 172 // create schema 173 fmt.Println("Creating", schemaFilename) 174 if err := initFile(schemaFilename, schemaDefault); err != nil { 175 return err 176 } 177 178 // create the package directory with a temporary file so that go recognises it as a package 179 // and autobinding doesn't error out 180 tmpPackageNameFile := "graph/model/_tmp_gqlgen_init.go" 181 if err := initFile(tmpPackageNameFile, "package model"); err != nil { 182 return err 183 } 184 defer os.Remove(tmpPackageNameFile) 185 186 var cfg *config.Config 187 if cfg, err = config.LoadConfig(configFilename); err != nil { 188 panic(err) 189 } 190 191 fmt.Println("Creating", serverFilename) 192 fmt.Println("Generating...") 193 if err := api.Generate(cfg, api.AddPlugin(servergen.New(serverFilename))); err != nil { 194 fmt.Fprintln(os.Stderr, err.Error()) 195 } 196 197 fmt.Printf("\nExec \"go run ./%s\" to start GraphQL server\n", serverFilename) 198 return nil 199 }, 200 } 201 202 var generateCmd = &cli.Command{ 203 Name: "generate", 204 Usage: "generate a graphql server based on schema", 205 Flags: []cli.Flag{ 206 &cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, 207 &cli.StringFlag{Name: "config, c", Usage: "the config filename"}, 208 }, 209 Action: func(ctx *cli.Context) error { 210 var cfg *config.Config 211 var err error 212 if configFilename := ctx.String("config"); configFilename != "" { 213 cfg, err = config.LoadConfig(configFilename) 214 if err != nil { 215 return err 216 } 217 } else { 218 cfg, err = config.LoadConfigFromDefaultLocations() 219 if errors.Is(err, fs.ErrNotExist) { 220 cfg, err = config.LoadDefaultConfig() 221 } 222 223 if err != nil { 224 return err 225 } 226 } 227 228 if err = api.Generate(cfg); err != nil { 229 return err 230 } 231 return nil 232 }, 233 } 234 235 var versionCmd = &cli.Command{ 236 Name: "version", 237 Usage: "print the version string", 238 Action: func(ctx *cli.Context) error { 239 fmt.Println(graphql.Version) 240 return nil 241 }, 242 } 243 244 func main() { 245 app := cli.NewApp() 246 app.Name = "gqlgen" 247 app.Usage = generateCmd.Usage 248 app.Description = "This is a library for quickly creating strictly typed graphql servers in golang. See https://gqlgen.com/ for a getting started guide." 249 app.HideVersion = true 250 app.Flags = generateCmd.Flags 251 app.Version = graphql.Version 252 app.Before = func(context *cli.Context) error { 253 if context.Bool("verbose") { 254 log.SetFlags(0) 255 } else { 256 log.SetOutput(ioutil.Discard) 257 } 258 return nil 259 } 260 261 app.Action = generateCmd.Action 262 app.Commands = []*cli.Command{ 263 generateCmd, 264 initCmd, 265 versionCmd, 266 } 267 268 if err := app.Run(os.Args); err != nil { 269 fmt.Fprint(os.Stderr, err.Error()+"\n") 270 os.Exit(1) 271 } 272 }