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  }