input_build.go

 1package codegen
 2
 3import (
 4	"go/types"
 5	"sort"
 6
 7	"github.com/pkg/errors"
 8	"github.com/vektah/gqlparser/ast"
 9	"golang.org/x/tools/go/loader"
10)
11
12func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program) (Objects, error) {
13	var inputs Objects
14
15	for _, typ := range cfg.schema.Types {
16		switch typ.Kind {
17		case ast.InputObject:
18			input, err := cfg.buildInput(namedTypes, typ)
19			if err != nil {
20				return nil, err
21			}
22
23			def, err := findGoType(prog, input.Package, input.GoType)
24			if err != nil {
25				return nil, errors.Wrap(err, "cannot find type")
26			}
27			if def != nil {
28				input.Marshaler = buildInputMarshaler(typ, def)
29				bindErrs := bindObject(def.Type(), input, cfg.StructTag)
30				if len(bindErrs) > 0 {
31					return nil, bindErrs
32				}
33			}
34
35			inputs = append(inputs, input)
36		}
37	}
38
39	sort.Slice(inputs, func(i, j int) bool {
40		return inputs[i].GQLType < inputs[j].GQLType
41	})
42
43	return inputs, nil
44}
45
46func (cfg *Config) buildInput(types NamedTypes, typ *ast.Definition) (*Object, error) {
47	obj := &Object{NamedType: types[typ.Name]}
48	typeEntry, entryExists := cfg.Models[typ.Name]
49
50	for _, field := range typ.Fields {
51		newField := Field{
52			GQLName: field.Name,
53			Type:    types.getType(field.Type),
54			Object:  obj,
55		}
56
57		if entryExists {
58			if typeField, ok := typeEntry.Fields[field.Name]; ok {
59				newField.GoFieldName = typeField.FieldName
60			}
61		}
62
63		if field.DefaultValue != nil {
64			var err error
65			newField.Default, err = field.DefaultValue.Value(nil)
66			if err != nil {
67				return nil, errors.Errorf("default value for %s.%s is not valid: %s", typ.Name, field.Name, err.Error())
68			}
69		}
70
71		if !newField.Type.IsInput && !newField.Type.IsScalar {
72			return nil, errors.Errorf("%s cannot be used as a field of %s. only input and scalar types are allowed", newField.GQLType, obj.GQLType)
73		}
74
75		obj.Fields = append(obj.Fields, newField)
76
77	}
78	return obj, nil
79}
80
81// if user has implemented an UnmarshalGQL method on the input type manually, use it
82// otherwise we will generate one.
83func buildInputMarshaler(typ *ast.Definition, def types.Object) *Ref {
84	switch def := def.(type) {
85	case *types.TypeName:
86		namedType := def.Type().(*types.Named)
87		for i := 0; i < namedType.NumMethods(); i++ {
88			method := namedType.Method(i)
89			if method.Name() == "UnmarshalGQL" {
90				return nil
91			}
92		}
93	}
94
95	return &Ref{GoType: typ.Name}
96}