github.com/fortexxx/gqlgen@v0.10.3-0.20191216030626-ca5ea8b21ead/codegen/data.go (about)

     1  package codegen
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  
     8  	"github.com/99designs/gqlgen/codegen/config"
     9  	"github.com/pkg/errors"
    10  	"github.com/vektah/gqlparser/ast"
    11  	"github.com/vektah/gqlparser/formatter"
    12  )
    13  
    14  // Data is a unified model of the code to be generated. Plugins may modify this structure to do things like implement
    15  // resolvers or directives automatically (eg grpc, validation)
    16  type Data struct {
    17  	Config          *config.Config
    18  	Schema          *ast.Schema
    19  	SchemaStr       map[string]string
    20  	Directives      DirectiveList
    21  	Objects         Objects
    22  	Inputs          Objects
    23  	Interfaces      map[string]*Interface
    24  	ReferencedTypes map[string]*config.TypeReference
    25  	ComplexityRoots map[string]*Object
    26  
    27  	QueryRoot        *Object
    28  	MutationRoot     *Object
    29  	SubscriptionRoot *Object
    30  }
    31  
    32  type builder struct {
    33  	Config     *config.Config
    34  	Schema     *ast.Schema
    35  	SchemaStr  map[string]string
    36  	Binder     *config.Binder
    37  	Directives map[string]*Directive
    38  }
    39  
    40  type SchemaMutator interface {
    41  	MutateSchema(s *ast.Schema) error
    42  }
    43  
    44  func BuildData(cfg *config.Config, plugins []SchemaMutator) (*Data, error) {
    45  	b := builder{
    46  		Config: cfg,
    47  	}
    48  
    49  	var err error
    50  	b.Schema, err = cfg.LoadSchema()
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	err = cfg.Check()
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	err = cfg.Autobind(b.Schema)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	cfg.InjectBuiltins(b.Schema)
    66  
    67  	for _, p := range plugins {
    68  		err = p.MutateSchema(b.Schema)
    69  		if err != nil {
    70  			return nil, fmt.Errorf("error running MutateSchema: %v", err)
    71  		}
    72  	}
    73  
    74  	b.Binder, err = b.Config.NewBinder(b.Schema)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	b.Directives, err = b.buildDirectives()
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	dataDirectives := make(map[string]*Directive)
    85  	for name, d := range b.Directives {
    86  		if !d.Builtin {
    87  			dataDirectives[name] = d
    88  		}
    89  	}
    90  
    91  	s := Data{
    92  		Config:     cfg,
    93  		Directives: dataDirectives,
    94  		Schema:     b.Schema,
    95  		SchemaStr:  b.SchemaStr,
    96  		Interfaces: map[string]*Interface{},
    97  	}
    98  
    99  	for _, schemaType := range b.Schema.Types {
   100  		switch schemaType.Kind {
   101  		case ast.Object:
   102  			obj, err := b.buildObject(schemaType)
   103  			if err != nil {
   104  				return nil, errors.Wrap(err, "unable to build object definition")
   105  			}
   106  
   107  			s.Objects = append(s.Objects, obj)
   108  		case ast.InputObject:
   109  			input, err := b.buildObject(schemaType)
   110  			if err != nil {
   111  				return nil, errors.Wrap(err, "unable to build input definition")
   112  			}
   113  
   114  			s.Inputs = append(s.Inputs, input)
   115  
   116  		case ast.Union, ast.Interface:
   117  			s.Interfaces[schemaType.Name] = b.buildInterface(schemaType)
   118  		}
   119  	}
   120  
   121  	if s.Schema.Query != nil {
   122  		s.QueryRoot = s.Objects.ByName(s.Schema.Query.Name)
   123  	} else {
   124  		return nil, fmt.Errorf("query entry point missing")
   125  	}
   126  
   127  	if s.Schema.Mutation != nil {
   128  		s.MutationRoot = s.Objects.ByName(s.Schema.Mutation.Name)
   129  	}
   130  
   131  	if s.Schema.Subscription != nil {
   132  		s.SubscriptionRoot = s.Objects.ByName(s.Schema.Subscription.Name)
   133  	}
   134  
   135  	if err := b.injectIntrospectionRoots(&s); err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	s.ReferencedTypes = b.buildTypes()
   140  
   141  	sort.Slice(s.Objects, func(i, j int) bool {
   142  		return s.Objects[i].Definition.Name < s.Objects[j].Definition.Name
   143  	})
   144  
   145  	sort.Slice(s.Inputs, func(i, j int) bool {
   146  		return s.Inputs[i].Definition.Name < s.Inputs[j].Definition.Name
   147  	})
   148  
   149  	if b.Binder.SawInvalid {
   150  		// if we have a syntax error, show it
   151  		if len(b.Binder.PkgErrors) > 0 {
   152  			return nil, b.Binder.PkgErrors
   153  		}
   154  
   155  		// otherwise show a generic error message
   156  		return nil, fmt.Errorf("invalid types were encountered while traversing the go source code, this probably means the invalid code generated isnt correct. add try adding -v to debug")
   157  	}
   158  
   159  	var buf bytes.Buffer
   160  	formatter.NewFormatter(&buf).FormatSchema(b.Schema)
   161  	s.SchemaStr = map[string]string{"schema.graphql": buf.String()}
   162  
   163  	return &s, nil
   164  }
   165  
   166  func (b *builder) injectIntrospectionRoots(s *Data) error {
   167  	obj := s.Objects.ByName(b.Schema.Query.Name)
   168  	if obj == nil {
   169  		return fmt.Errorf("root query type must be defined")
   170  	}
   171  
   172  	__type, err := b.buildField(obj, &ast.FieldDefinition{
   173  		Name: "__type",
   174  		Type: ast.NamedType("__Type", nil),
   175  		Arguments: []*ast.ArgumentDefinition{
   176  			{
   177  				Name: "name",
   178  				Type: ast.NonNullNamedType("String", nil),
   179  			},
   180  		},
   181  	})
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	__schema, err := b.buildField(obj, &ast.FieldDefinition{
   187  		Name: "__schema",
   188  		Type: ast.NamedType("__Schema", nil),
   189  	})
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	obj.Fields = append(obj.Fields, __type, __schema)
   195  
   196  	return nil
   197  }