1//go:generate go run ./inliner/inliner.go
2
3package validator
4
5import (
6 "strconv"
7
8 . "github.com/vektah/gqlparser/ast"
9 "github.com/vektah/gqlparser/gqlerror"
10 "github.com/vektah/gqlparser/parser"
11)
12
13func LoadSchema(inputs ...*Source) (*Schema, *gqlerror.Error) {
14 ast := &SchemaDocument{}
15 for _, input := range inputs {
16 inputAst, err := parser.ParseSchema(input)
17 if err != nil {
18 return nil, err
19 }
20
21 ast.Merge(inputAst)
22 }
23
24 schema := Schema{
25 Types: map[string]*Definition{},
26 Directives: map[string]*DirectiveDefinition{},
27 PossibleTypes: map[string][]*Definition{},
28 }
29
30 for i, def := range ast.Definitions {
31 if schema.Types[def.Name] != nil {
32 return nil, gqlerror.ErrorPosf(def.Position, "Cannot redeclare type %s.", def.Name)
33 }
34 schema.Types[def.Name] = ast.Definitions[i]
35
36 if def.Kind != Interface {
37 for _, intf := range def.Interfaces {
38 schema.AddPossibleType(intf, ast.Definitions[i])
39 }
40 schema.AddPossibleType(def.Name, ast.Definitions[i])
41 }
42 }
43
44 for _, ext := range ast.Extensions {
45 def := schema.Types[ext.Name]
46 if def == nil {
47 return nil, gqlerror.ErrorPosf(ext.Position, "Cannot extend type %s because it does not exist.", ext.Name)
48 }
49
50 if def.Kind != ext.Kind {
51 return nil, gqlerror.ErrorPosf(ext.Position, "Cannot extend type %s because the base type is a %s, not %s.", ext.Name, def.Kind, ext.Kind)
52 }
53
54 def.Directives = append(def.Directives, ext.Directives...)
55 def.Interfaces = append(def.Interfaces, ext.Interfaces...)
56 def.Fields = append(def.Fields, ext.Fields...)
57 def.Types = append(def.Types, ext.Types...)
58 def.EnumValues = append(def.EnumValues, ext.EnumValues...)
59 }
60
61 for i, dir := range ast.Directives {
62 if schema.Directives[dir.Name] != nil {
63 return nil, gqlerror.ErrorPosf(dir.Position, "Cannot redeclare directive %s.", dir.Name)
64 }
65 schema.Directives[dir.Name] = ast.Directives[i]
66 }
67
68 if len(ast.Schema) > 1 {
69 return nil, gqlerror.ErrorPosf(ast.Schema[1].Position, "Cannot have multiple schema entry points, consider schema extensions instead.")
70 }
71
72 if len(ast.Schema) == 1 {
73 for _, entrypoint := range ast.Schema[0].OperationTypes {
74 def := schema.Types[entrypoint.Type]
75 if def == nil {
76 return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
77 }
78 switch entrypoint.Operation {
79 case Query:
80 schema.Query = def
81 case Mutation:
82 schema.Mutation = def
83 case Subscription:
84 schema.Subscription = def
85 }
86 }
87 }
88
89 for _, ext := range ast.SchemaExtension {
90 for _, entrypoint := range ext.OperationTypes {
91 def := schema.Types[entrypoint.Type]
92 if def == nil {
93 return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type)
94 }
95 switch entrypoint.Operation {
96 case Query:
97 schema.Query = def
98 case Mutation:
99 schema.Mutation = def
100 case Subscription:
101 schema.Subscription = def
102 }
103 }
104 }
105
106 for _, typ := range schema.Types {
107 err := validateDefinition(&schema, typ)
108 if err != nil {
109 return nil, err
110 }
111 }
112
113 for _, dir := range schema.Directives {
114 err := validateDirective(&schema, dir)
115 if err != nil {
116 return nil, err
117 }
118 }
119
120 if schema.Query == nil && schema.Types["Query"] != nil {
121 schema.Query = schema.Types["Query"]
122 }
123
124 if schema.Mutation == nil && schema.Types["Mutation"] != nil {
125 schema.Mutation = schema.Types["Mutation"]
126 }
127
128 if schema.Subscription == nil && schema.Types["Subscription"] != nil {
129 schema.Subscription = schema.Types["Subscription"]
130 }
131
132 if schema.Query != nil {
133 schema.Query.Fields = append(
134 schema.Query.Fields,
135 &FieldDefinition{
136 Name: "__schema",
137 Type: NonNullNamedType("__Schema", nil),
138 },
139 &FieldDefinition{
140 Name: "__type",
141 Type: NonNullNamedType("__Type", nil),
142 Arguments: ArgumentDefinitionList{
143 {Name: "name", Type: NamedType("String", nil)},
144 },
145 },
146 )
147 }
148
149 return &schema, nil
150}
151
152func validateDirective(schema *Schema, def *DirectiveDefinition) *gqlerror.Error {
153 return validateArgs(schema, def.Arguments, def)
154}
155
156func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error {
157 for _, field := range def.Fields {
158 if err := validateTypeRef(schema, field.Type); err != nil {
159 return err
160 }
161 if err := validateArgs(schema, field.Arguments, nil); err != nil {
162 return err
163 }
164 if err := validateDirectives(schema, field.Directives, nil); err != nil {
165 return err
166 }
167 }
168
169 for _, intf := range def.Interfaces {
170 intDef := schema.Types[intf]
171 if intDef == nil {
172 return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intf))
173 }
174 if intDef.Kind != Interface {
175 return gqlerror.ErrorPosf(def.Position, "%s is a non interface type %s.", strconv.Quote(intf), intDef.Kind)
176 }
177 }
178
179 return validateDirectives(schema, def.Directives, nil)
180}
181
182func validateTypeRef(schema *Schema, typ *Type) *gqlerror.Error {
183 if schema.Types[typ.Name()] == nil {
184 return gqlerror.ErrorPosf(typ.Position, "Undefined type %s.", typ.Name())
185 }
186 return nil
187}
188
189func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective *DirectiveDefinition) *gqlerror.Error {
190 for _, arg := range args {
191 if err := validateTypeRef(schema, arg.Type); err != nil {
192 return err
193 }
194 if err := validateDirectives(schema, arg.Directives, currentDirective); err != nil {
195 return err
196 }
197 }
198 return nil
199}
200
201func validateDirectives(schema *Schema, dirs DirectiveList, currentDirective *DirectiveDefinition) *gqlerror.Error {
202 for _, dir := range dirs {
203 if currentDirective != nil && dir.Name == currentDirective.Name {
204 return gqlerror.ErrorPosf(dir.Position, "Directive %s cannot refer to itself.", currentDirective.Name)
205 }
206 if schema.Directives[dir.Name] == nil {
207 return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name)
208 }
209 dir.Definition = schema.Directives[dir.Name]
210 }
211 return nil
212}