github.com/mstephano/gqlgen-schemagen@v0.0.0-20230113041936-dd2cd4ea46aa/api/generate.go (about) 1 package api 2 3 import ( 4 "fmt" 5 "regexp" 6 "syscall" 7 8 "github.com/mstephano/gqlgen-schemagen/codegen" 9 "github.com/mstephano/gqlgen-schemagen/codegen/config" 10 "github.com/mstephano/gqlgen-schemagen/plugin" 11 "github.com/mstephano/gqlgen-schemagen/plugin/federation" 12 "github.com/mstephano/gqlgen-schemagen/plugin/modelgen" 13 "github.com/mstephano/gqlgen-schemagen/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, err := codegen.BuildData(cfg) 87 if err != nil { 88 return fmt.Errorf("merging type systems failed: %w", err) 89 } 90 91 if err = codegen.GenerateCode(data); err != nil { 92 return fmt.Errorf("generating core failed: %w", err) 93 } 94 95 if !cfg.SkipModTidy { 96 if err = cfg.Packages.ModTidy(); err != nil { 97 return fmt.Errorf("tidy failed: %w", err) 98 } 99 } 100 101 for _, p := range plugins { 102 if mut, ok := p.(plugin.CodeGenerator); ok { 103 err := mut.GenerateCode(data) 104 if err != nil { 105 return fmt.Errorf("%s: %w", p.Name(), err) 106 } 107 } 108 } 109 110 if err = codegen.GenerateCode(data); err != nil { 111 return fmt.Errorf("generating core failed: %w", err) 112 } 113 114 if !cfg.SkipValidation { 115 if err := validate(cfg); err != nil { 116 return fmt.Errorf("validation failed: %w", err) 117 } 118 } 119 120 return nil 121 } 122 123 func validate(cfg *config.Config) error { 124 roots := []string{cfg.Exec.ImportPath()} 125 if cfg.Model.IsDefined() { 126 roots = append(roots, cfg.Model.ImportPath()) 127 } 128 129 if cfg.Resolver.IsDefined() { 130 roots = append(roots, cfg.Resolver.ImportPath()) 131 } 132 133 cfg.Packages.LoadAll(roots...) 134 errs := cfg.Packages.Errors() 135 if len(errs) > 0 { 136 return errs 137 } 138 return nil 139 }