aboutsummaryrefslogblamecommitdiffstats
path: root/vendor/github.com/99designs/gqlgen/codegen/build.go
blob: 42dedbf872bb813d9ecf3d38c2e4fef5586a23c1 (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
	SchemaFilename   string
	Directives       []*Directive
}

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

type ResolverBuild struct {
	PackageName   string
	Imports       []*Import
	ResolverType  string
	Objects       Objects
	ResolverFound bool
}

type ServerBuild struct {
	PackageName         string
	Imports             []*Import
	ExecPackageName     string
	ResolverPackageName string
}

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

	progLoader := newLoader(namedTypes, true)
	prog, err := progLoader.Load()
	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, imports)
	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) resolver() (*ResolverBuild, error) {
	progLoader := newLoader(cfg.buildNamedTypes(), true)
	progLoader.Import(cfg.Resolver.ImportPath())

	prog, err := progLoader.Load()
	if err != nil {
		return nil, err
	}

	destDir := cfg.Resolver.Dir()

	namedTypes := cfg.buildNamedTypes()
	imports := buildImports(namedTypes, destDir)
	imports.add(cfg.Exec.ImportPath())
	imports.add("github.com/99designs/gqlgen/handler") // avoid import github.com/vektah/gqlgen/handler

	cfg.bindTypes(imports, namedTypes, destDir, prog)

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

	def, _ := findGoType(prog, cfg.Resolver.ImportPath(), cfg.Resolver.Type)
	resolverFound := def != nil

	return &ResolverBuild{
		PackageName:   cfg.Resolver.Package,
		Imports:       imports.finalize(),
		Objects:       objects,
		ResolverType:  cfg.Resolver.Type,
		ResolverFound: resolverFound,
	}, nil
}

func (cfg *Config) server(destDir string) *ServerBuild {
	imports := buildImports(NamedTypes{}, destDir)
	imports.add(cfg.Exec.ImportPath())
	imports.add(cfg.Resolver.ImportPath())

	return &ServerBuild{
		PackageName:         cfg.Resolver.Package,
		Imports:             imports.finalize(),
		ExecPackageName:     cfg.Exec.Package,
		ResolverPackageName: cfg.Resolver.Package,
	}
}

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

	progLoader := newLoader(namedTypes, true)
	prog, err := progLoader.Load()
	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
	}
	directives, err := cfg.buildDirectives(namedTypes)
	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,
		SchemaFilename: cfg.SchemaFilename,
		Directives:     directives,
	}

	if cfg.schema.Query != nil {
		b.QueryRoot = b.Objects.ByName(cfg.schema.Query.Name)
	} else {
		return b, fmt.Errorf("query entry point missing")
	}

	if cfg.schema.Mutation != nil {
		b.MutationRoot = b.Objects.ByName(cfg.schema.Mutation.Name)
	}

	if cfg.schema.Subscription != nil {
		b.SubscriptionRoot = b.Objects.ByName(cfg.schema.Subscription.Name)
	}
	return b, nil
}

func (cfg *Config) validate() error {
	progLoader := newLoader(cfg.buildNamedTypes(), false)
	_, err := progLoader.Load()
	return err
}

func newLoader(namedTypes NamedTypes, allowErrors bool) loader.Config {
	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
}

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
}