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