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}