aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/vektah/gqlgen/codegen/build.go
blob: d56fc06f1f569ba631268104946e241c8cff8b4d (plain) (tree)




































































































































































                                                                                                                 
package codegen

import (
	"fmt"
	"go/build"
	"go/types"
	"os"

	"github.com/pkg/errors"
	"golang.org/x/tools/go/loader"
)

type Build struct {
	PackageName      string
	Objects          Objects
	Inputs           Objects
	Interfaces       []*Interface
	Imports          []*Import
	QueryRoot        *Object
	MutationRoot     *Object
	SubscriptionRoot *Object
	SchemaRaw        string
}

type ModelBuild struct {
	PackageName string
	Imports     []*Import
	Models      []Model
	Enums       []Enum
}

// Create a list of models that need to be generated
func (cfg *Config) models() (*ModelBuild, error) {
	namedTypes := cfg.buildNamedTypes()

	prog, err := cfg.loadProgram(namedTypes, true)
	if err != nil {
		return nil, errors.Wrap(err, "loading failed")
	}
	imports := buildImports(namedTypes, cfg.Model.Dir())

	cfg.bindTypes(imports, namedTypes, cfg.Model.Dir(), prog)

	models, err := cfg.buildModels(namedTypes, prog)
	if err != nil {
		return nil, err
	}
	return &ModelBuild{
		PackageName: cfg.Model.Package,
		Models:      models,
		Enums:       cfg.buildEnums(namedTypes),
		Imports:     imports.finalize(),
	}, nil
}

// bind a schema together with some code to generate a Build
func (cfg *Config) bind() (*Build, error) {
	namedTypes := cfg.buildNamedTypes()

	prog, err := cfg.loadProgram(namedTypes, true)
	if err != nil {
		return nil, errors.Wrap(err, "loading failed")
	}

	imports := buildImports(namedTypes, cfg.Exec.Dir())
	cfg.bindTypes(imports, namedTypes, cfg.Exec.Dir(), prog)

	objects, err := cfg.buildObjects(namedTypes, prog, imports)
	if err != nil {
		return nil, err
	}

	inputs, err := cfg.buildInputs(namedTypes, prog, imports)
	if err != nil {
		return nil, err
	}

	b := &Build{
		PackageName: cfg.Exec.Package,
		Objects:     objects,
		Interfaces:  cfg.buildInterfaces(namedTypes, prog),
		Inputs:      inputs,
		Imports:     imports.finalize(),
		SchemaRaw:   cfg.SchemaStr,
	}

	if qr, ok := cfg.schema.EntryPoints["query"]; ok {
		b.QueryRoot = b.Objects.ByName(qr.TypeName())
	}

	if mr, ok := cfg.schema.EntryPoints["mutation"]; ok {
		b.MutationRoot = b.Objects.ByName(mr.TypeName())
	}

	if sr, ok := cfg.schema.EntryPoints["subscription"]; ok {
		b.SubscriptionRoot = b.Objects.ByName(sr.TypeName())
	}

	if b.QueryRoot == nil {
		return b, fmt.Errorf("query entry point missing")
	}

	// Poke a few magic methods into query
	q := b.Objects.ByName(b.QueryRoot.GQLType)
	q.Fields = append(q.Fields, Field{
		Type:         &Type{namedTypes["__Schema"], []string{modPtr}, nil},
		GQLName:      "__schema",
		NoErr:        true,
		GoMethodName: "ec.introspectSchema",
		Object:       q,
	})
	q.Fields = append(q.Fields, Field{
		Type:         &Type{namedTypes["__Type"], []string{modPtr}, nil},
		GQLName:      "__type",
		NoErr:        true,
		GoMethodName: "ec.introspectType",
		Args: []FieldArgument{
			{GQLName: "name", Type: &Type{namedTypes["String"], []string{}, nil}, Object: &Object{}},
		},
		Object: q,
	})

	return b, nil
}

func (cfg *Config) validate() error {
	namedTypes := cfg.buildNamedTypes()

	_, err := cfg.loadProgram(namedTypes, false)
	return err
}

func (cfg *Config) loadProgram(namedTypes NamedTypes, allowErrors bool) (*loader.Program, error) {
	conf := loader.Config{}
	if allowErrors {
		conf = loader.Config{
			AllowErrors: true,
			TypeChecker: types.Config{
				Error: func(e error) {},
			},
		}
	}
	for _, imp := range ambientImports {
		conf.Import(imp)
	}

	for _, imp := range namedTypes {
		if imp.Package != "" {
			conf.Import(imp.Package)
		}
	}

	return conf.Load()
}

func resolvePkg(pkgName string) (string, error) {
	cwd, _ := os.Getwd()

	pkg, err := build.Default.Import(pkgName, cwd, build.FindOnly)
	if err != nil {
		return "", err
	}

	return pkg.ImportPath, nil
}