build.go

  1package codegen
  2
  3import (
  4	"fmt"
  5	"go/build"
  6	"go/types"
  7	"os"
  8
  9	"github.com/pkg/errors"
 10	"golang.org/x/tools/go/loader"
 11)
 12
 13type Build struct {
 14	PackageName      string
 15	Objects          Objects
 16	Inputs           Objects
 17	Interfaces       []*Interface
 18	Imports          []*Import
 19	QueryRoot        *Object
 20	MutationRoot     *Object
 21	SubscriptionRoot *Object
 22	SchemaRaw        string
 23}
 24
 25type ModelBuild struct {
 26	PackageName string
 27	Imports     []*Import
 28	Models      []Model
 29	Enums       []Enum
 30}
 31
 32// Create a list of models that need to be generated
 33func (cfg *Config) models() (*ModelBuild, error) {
 34	namedTypes := cfg.buildNamedTypes()
 35
 36	prog, err := cfg.loadProgram(namedTypes, true)
 37	if err != nil {
 38		return nil, errors.Wrap(err, "loading failed")
 39	}
 40	imports := buildImports(namedTypes, cfg.Model.Dir())
 41
 42	cfg.bindTypes(imports, namedTypes, cfg.Model.Dir(), prog)
 43
 44	models, err := cfg.buildModels(namedTypes, prog)
 45	if err != nil {
 46		return nil, err
 47	}
 48	return &ModelBuild{
 49		PackageName: cfg.Model.Package,
 50		Models:      models,
 51		Enums:       cfg.buildEnums(namedTypes),
 52		Imports:     imports.finalize(),
 53	}, nil
 54}
 55
 56// bind a schema together with some code to generate a Build
 57func (cfg *Config) bind() (*Build, error) {
 58	namedTypes := cfg.buildNamedTypes()
 59
 60	prog, err := cfg.loadProgram(namedTypes, true)
 61	if err != nil {
 62		return nil, errors.Wrap(err, "loading failed")
 63	}
 64
 65	imports := buildImports(namedTypes, cfg.Exec.Dir())
 66	cfg.bindTypes(imports, namedTypes, cfg.Exec.Dir(), prog)
 67
 68	objects, err := cfg.buildObjects(namedTypes, prog, imports)
 69	if err != nil {
 70		return nil, err
 71	}
 72
 73	inputs, err := cfg.buildInputs(namedTypes, prog, imports)
 74	if err != nil {
 75		return nil, err
 76	}
 77
 78	b := &Build{
 79		PackageName: cfg.Exec.Package,
 80		Objects:     objects,
 81		Interfaces:  cfg.buildInterfaces(namedTypes, prog),
 82		Inputs:      inputs,
 83		Imports:     imports.finalize(),
 84		SchemaRaw:   cfg.SchemaStr,
 85	}
 86
 87	if qr, ok := cfg.schema.EntryPoints["query"]; ok {
 88		b.QueryRoot = b.Objects.ByName(qr.TypeName())
 89	}
 90
 91	if mr, ok := cfg.schema.EntryPoints["mutation"]; ok {
 92		b.MutationRoot = b.Objects.ByName(mr.TypeName())
 93	}
 94
 95	if sr, ok := cfg.schema.EntryPoints["subscription"]; ok {
 96		b.SubscriptionRoot = b.Objects.ByName(sr.TypeName())
 97	}
 98
 99	if b.QueryRoot == nil {
100		return b, fmt.Errorf("query entry point missing")
101	}
102
103	// Poke a few magic methods into query
104	q := b.Objects.ByName(b.QueryRoot.GQLType)
105	q.Fields = append(q.Fields, Field{
106		Type:         &Type{namedTypes["__Schema"], []string{modPtr}, nil},
107		GQLName:      "__schema",
108		NoErr:        true,
109		GoMethodName: "ec.introspectSchema",
110		Object:       q,
111	})
112	q.Fields = append(q.Fields, Field{
113		Type:         &Type{namedTypes["__Type"], []string{modPtr}, nil},
114		GQLName:      "__type",
115		NoErr:        true,
116		GoMethodName: "ec.introspectType",
117		Args: []FieldArgument{
118			{GQLName: "name", Type: &Type{namedTypes["String"], []string{}, nil}, Object: &Object{}},
119		},
120		Object: q,
121	})
122
123	return b, nil
124}
125
126func (cfg *Config) validate() error {
127	namedTypes := cfg.buildNamedTypes()
128
129	_, err := cfg.loadProgram(namedTypes, false)
130	return err
131}
132
133func (cfg *Config) loadProgram(namedTypes NamedTypes, allowErrors bool) (*loader.Program, error) {
134	conf := loader.Config{}
135	if allowErrors {
136		conf = loader.Config{
137			AllowErrors: true,
138			TypeChecker: types.Config{
139				Error: func(e error) {},
140			},
141		}
142	}
143	for _, imp := range ambientImports {
144		conf.Import(imp)
145	}
146
147	for _, imp := range namedTypes {
148		if imp.Package != "" {
149			conf.Import(imp.Package)
150		}
151	}
152
153	return conf.Load()
154}
155
156func resolvePkg(pkgName string) (string, error) {
157	cwd, _ := os.Getwd()
158
159	pkg, err := build.Default.Import(pkgName, cwd, build.FindOnly)
160	if err != nil {
161		return "", err
162	}
163
164	return pkg.ImportPath, nil
165}