github.com/niko0xdev/gqlgen@v0.17.55-0.20240120102243-2ecff98c3e37/api/generate.go (about) 1 package api 2 3 import ( 4 "fmt" 5 "regexp" 6 "syscall" 7 8 "github.com/niko0xdev/gqlgen/codegen" 9 "github.com/niko0xdev/gqlgen/codegen/config" 10 "github.com/niko0xdev/gqlgen/plugin" 11 "github.com/niko0xdev/gqlgen/plugin/federation" 12 "github.com/niko0xdev/gqlgen/plugin/modelgen" 13 "github.com/niko0xdev/gqlgen/plugin/resolvergen" 14 ) 15 16 func Generate(cfg *config.Config, option ...Option) error { 17 _ = syscall.Unlink(cfg.Exec.Filename) 18 if cfg.Model.IsDefined() { 19 _ = syscall.Unlink(cfg.Model.Filename) 20 } 21 22 plugins := []plugin.Plugin{} 23 if cfg.Model.IsDefined() { 24 plugins = append(plugins, modelgen.New()) 25 } 26 plugins = append(plugins, resolvergen.New()) 27 if cfg.Federation.IsDefined() { 28 if cfg.Federation.Version == 0 { // default to using the user's choice of version, but if unset, try to sort out which federation version to use 29 urlRegex := regexp.MustCompile(`(?s)@link.*\(.*url:.*?"(.*?)"[^)]+\)`) // regex to grab the url of a link directive, should it exist 30 31 // check the sources, and if one is marked as federation v2, we mark the entirety to be generated using that format 32 for _, v := range cfg.Sources { 33 cfg.Federation.Version = 1 34 urlString := urlRegex.FindStringSubmatch(v.Input) 35 if urlString != nil && urlString[1] == "https://specs.apollo.dev/federation/v2.0" { 36 cfg.Federation.Version = 2 37 break 38 } 39 } 40 } 41 plugins = append([]plugin.Plugin{federation.New(cfg.Federation.Version)}, plugins...) 42 } 43 44 for _, o := range option { 45 o(cfg, &plugins) 46 } 47 48 for _, p := range plugins { 49 if inj, ok := p.(plugin.EarlySourceInjector); ok { 50 if s := inj.InjectSourceEarly(); s != nil { 51 cfg.Sources = append(cfg.Sources, s) 52 } 53 } 54 } 55 56 if err := cfg.LoadSchema(); err != nil { 57 return fmt.Errorf("failed to load schema: %w", err) 58 } 59 60 for _, p := range plugins { 61 if inj, ok := p.(plugin.LateSourceInjector); ok { 62 if s := inj.InjectSourceLate(cfg.Schema); s != nil { 63 cfg.Sources = append(cfg.Sources, s) 64 } 65 } 66 } 67 68 // LoadSchema again now we have everything 69 if err := cfg.LoadSchema(); err != nil { 70 return fmt.Errorf("failed to load schema: %w", err) 71 } 72 73 if err := cfg.Init(); err != nil { 74 return fmt.Errorf("generating core failed: %w", err) 75 } 76 77 for _, p := range plugins { 78 if mut, ok := p.(plugin.ConfigMutator); ok { 79 err := mut.MutateConfig(cfg) 80 if err != nil { 81 return fmt.Errorf("%s: %w", p.Name(), err) 82 } 83 } 84 } 85 // Merge again now that the generated models have been injected into the typemap 86 data_plugins := make([]interface{}, len(plugins)) 87 for index := range plugins { 88 data_plugins[index] = plugins[index] 89 } 90 data, err := codegen.BuildData(cfg, data_plugins...) 91 if err != nil { 92 return fmt.Errorf("merging type systems failed: %w", err) 93 } 94 95 if err = codegen.GenerateCode(data); err != nil { 96 return fmt.Errorf("generating core failed: %w", err) 97 } 98 99 if !cfg.SkipModTidy { 100 if err = cfg.Packages.ModTidy(); err != nil { 101 return fmt.Errorf("tidy failed: %w", err) 102 } 103 } 104 105 for _, p := range plugins { 106 if mut, ok := p.(plugin.CodeGenerator); ok { 107 err := mut.GenerateCode(data) 108 if err != nil { 109 return fmt.Errorf("%s: %w", p.Name(), err) 110 } 111 } 112 } 113 114 if err = codegen.GenerateCode(data); err != nil { 115 return fmt.Errorf("generating core failed: %w", err) 116 } 117 118 if !cfg.SkipValidation { 119 if err := validate(cfg); err != nil { 120 return fmt.Errorf("validation failed: %w", err) 121 } 122 } 123 124 return nil 125 } 126 127 func validate(cfg *config.Config) error { 128 roots := []string{cfg.Exec.ImportPath()} 129 if cfg.Model.IsDefined() { 130 roots = append(roots, cfg.Model.ImportPath()) 131 } 132 133 if cfg.Resolver.IsDefined() { 134 roots = append(roots, cfg.Resolver.ImportPath()) 135 } 136 137 cfg.Packages.LoadAll(roots...) 138 errs := cfg.Packages.Errors() 139 if len(errs) > 0 { 140 return errs 141 } 142 return nil 143 }