1package graphql
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/graphql-go/graphql/language/ast"
8 "github.com/graphql-go/graphql/language/kinds"
9 "github.com/graphql-go/graphql/language/printer"
10 "github.com/graphql-go/graphql/language/visitor"
11)
12
13func fieldsConflictMessage(responseName string, reason conflictReason) string {
14 return fmt.Sprintf(`Fields "%v" conflict because %v. `+
15 `Use different aliases on the fields to fetch both if this was intentional.`,
16 responseName,
17 fieldsConflictReasonMessage(reason),
18 )
19}
20
21func fieldsConflictReasonMessage(message interface{}) string {
22 switch reason := message.(type) {
23 case string:
24 return reason
25 case conflictReason:
26 return fieldsConflictReasonMessage(reason.Message)
27 case []conflictReason:
28 messages := []string{}
29 for _, r := range reason {
30 messages = append(messages, fmt.Sprintf(
31 `subfields "%v" conflict because %v`,
32 r.Name,
33 fieldsConflictReasonMessage(r.Message),
34 ))
35 }
36 return strings.Join(messages, " and ")
37 }
38 return ""
39}
40
41// OverlappingFieldsCanBeMergedRule Overlapping fields can be merged
42//
43// A selection set is only valid if all fields (including spreading any
44// fragments) either correspond to distinct response names or can be merged
45// without ambiguity.
46func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRuleInstance {
47
48 // A memoization for when two fragments are compared "between" each other for
49 // conflicts. Two fragments may be compared many times, so memoizing this can
50 // dramatically improve the performance of this validator.
51 comparedSet := newPairSet()
52
53 // A cache for the "field map" and list of fragment names found in any given
54 // selection set. Selection sets may be asked for this information multiple
55 // times, so this improves the performance of this validator.
56 cacheMap := map[*ast.SelectionSet]*fieldsAndFragmentNames{}
57
58 visitorOpts := &visitor.VisitorOptions{
59 KindFuncMap: map[string]visitor.NamedVisitFuncs{
60 kinds.SelectionSet: {
61 Kind: func(p visitor.VisitFuncParams) (string, interface{}) {
62 if selectionSet, ok := p.Node.(*ast.SelectionSet); ok && selectionSet != nil {
63 parentType, _ := context.ParentType().(Named)
64
65 rule := &overlappingFieldsCanBeMergedRule{
66 context: context,
67 comparedSet: comparedSet,
68 cacheMap: cacheMap,
69 }
70 conflicts := rule.findConflictsWithinSelectionSet(parentType, selectionSet)
71 if len(conflicts) > 0 {
72 for _, c := range conflicts {
73 responseName := c.Reason.Name
74 reason := c.Reason
75 reportError(
76 context,
77 fieldsConflictMessage(responseName, reason),
78 append(c.FieldsLeft, c.FieldsRight...),
79 )
80 }
81 return visitor.ActionNoChange, nil
82 }
83 }
84 return visitor.ActionNoChange, nil
85 },
86 },
87 },
88 }
89 return &ValidationRuleInstance{
90 VisitorOpts: visitorOpts,
91 }
92}
93
94/**
95 * Algorithm:
96 *
97 * Conflicts occur when two fields exist in a query which will produce the same
98 * response name, but represent differing values, thus creating a conflict.
99 * The algorithm below finds all conflicts via making a series of comparisons
100 * between fields. In order to compare as few fields as possible, this makes
101 * a series of comparisons "within" sets of fields and "between" sets of fields.
102 *
103 * Given any selection set, a collection produces both a set of fields by
104 * also including all inline fragments, as well as a list of fragments
105 * referenced by fragment spreads.
106 *
107 * A) Each selection set represented in the document first compares "within" its
108 * collected set of fields, finding any conflicts between every pair of
109 * overlapping fields.
110 * Note: This is the *only time* that a the fields "within" a set are compared
111 * to each other. After this only fields "between" sets are compared.
112 *
113 * B) Also, if any fragment is referenced in a selection set, then a
114 * comparison is made "between" the original set of fields and the
115 * referenced fragment.
116 *
117 * C) Also, if multiple fragments are referenced, then comparisons
118 * are made "between" each referenced fragment.
119 *
120 * D) When comparing "between" a set of fields and a referenced fragment, first
121 * a comparison is made between each field in the original set of fields and
122 * each field in the the referenced set of fields.
123 *
124 * E) Also, if any fragment is referenced in the referenced selection set,
125 * then a comparison is made "between" the original set of fields and the
126 * referenced fragment (recursively referring to step D).
127 *
128 * F) When comparing "between" two fragments, first a comparison is made between
129 * each field in the first referenced set of fields and each field in the the
130 * second referenced set of fields.
131 *
132 * G) Also, any fragments referenced by the first must be compared to the
133 * second, and any fragments referenced by the second must be compared to the
134 * first (recursively referring to step F).
135 *
136 * H) When comparing two fields, if both have selection sets, then a comparison
137 * is made "between" both selection sets, first comparing the set of fields in
138 * the first selection set with the set of fields in the second.
139 *
140 * I) Also, if any fragment is referenced in either selection set, then a
141 * comparison is made "between" the other set of fields and the
142 * referenced fragment.
143 *
144 * J) Also, if two fragments are referenced in both selection sets, then a
145 * comparison is made "between" the two fragments.
146 *
147 */
148
149type overlappingFieldsCanBeMergedRule struct {
150 context *ValidationContext
151
152 // A memoization for when two fragments are compared "between" each other for
153 // conflicts. Two fragments may be compared many times, so memoizing this can
154 // dramatically improve the performance of this validator.
155 comparedSet *pairSet
156
157 // A cache for the "field map" and list of fragment names found in any given
158 // selection set. Selection sets may be asked for this information multiple
159 // times, so this improves the performance of this validator.
160 cacheMap map[*ast.SelectionSet]*fieldsAndFragmentNames
161}
162
163// Find all conflicts found "within" a selection set, including those found
164// via spreading in fragments. Called when visiting each SelectionSet in the
165// GraphQL Document.
166func (rule *overlappingFieldsCanBeMergedRule) findConflictsWithinSelectionSet(parentType Named, selectionSet *ast.SelectionSet) []conflict {
167 conflicts := []conflict{}
168
169 fieldsInfo := rule.getFieldsAndFragmentNames(parentType, selectionSet)
170
171 // (A) Find find all conflicts "within" the fields of this selection set.
172 // Note: this is the *only place* `collectConflictsWithin` is called.
173 conflicts = rule.collectConflictsWithin(conflicts, fieldsInfo)
174
175 // (B) Then collect conflicts between these fields and those represented by
176 // each spread fragment name found.
177 for i := 0; i < len(fieldsInfo.fragmentNames); i++ {
178
179 conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, false, fieldsInfo, fieldsInfo.fragmentNames[i])
180
181 // (C) Then compare this fragment with all other fragments found in this
182 // selection set to collect conflicts between fragments spread together.
183 // This compares each item in the list of fragment names to every other item
184 // in that same list (except for itself).
185 for k := i + 1; k < len(fieldsInfo.fragmentNames); k++ {
186 conflicts = rule.collectConflictsBetweenFragments(conflicts, false, fieldsInfo.fragmentNames[i], fieldsInfo.fragmentNames[k])
187 }
188 }
189 return conflicts
190}
191
192// Collect all conflicts found between a set of fields and a fragment reference
193// including via spreading in any nested fragments.
194func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetweenFieldsAndFragment(conflicts []conflict, areMutuallyExclusive bool, fieldsInfo *fieldsAndFragmentNames, fragmentName string) []conflict {
195 fragment := rule.context.Fragment(fragmentName)
196 if fragment == nil {
197 return conflicts
198 }
199
200 fieldsInfo2 := rule.getReferencedFieldsAndFragmentNames(fragment)
201
202 // (D) First collect any conflicts between the provided collection of fields
203 // and the collection of fields represented by the given fragment.
204 conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo, fieldsInfo2)
205
206 // (E) Then collect any conflicts between the provided collection of fields
207 // and any fragment names found in the given fragment.
208 for _, fragmentName2 := range fieldsInfo2.fragmentNames {
209 conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo2, fragmentName2)
210 }
211
212 return conflicts
213
214}
215
216// Collect all conflicts found between two fragments, including via spreading in
217// any nested fragments.
218func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetweenFragments(conflicts []conflict, areMutuallyExclusive bool, fragmentName1 string, fragmentName2 string) []conflict {
219 fragment1 := rule.context.Fragment(fragmentName1)
220 fragment2 := rule.context.Fragment(fragmentName2)
221
222 if fragment1 == nil || fragment2 == nil {
223 return conflicts
224 }
225
226 // No need to compare a fragment to itself.
227 if fragment1 == fragment2 {
228 return conflicts
229 }
230
231 // Memoize so two fragments are not compared for conflicts more than once.
232 if rule.comparedSet.Has(fragmentName1, fragmentName2, areMutuallyExclusive) {
233 return conflicts
234 }
235 rule.comparedSet.Add(fragmentName1, fragmentName2, areMutuallyExclusive)
236
237 fieldsInfo1 := rule.getReferencedFieldsAndFragmentNames(fragment1)
238 fieldsInfo2 := rule.getReferencedFieldsAndFragmentNames(fragment2)
239
240 // (F) First, collect all conflicts between these two collections of fields
241 // (not including any nested fragments).
242 conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo1, fieldsInfo2)
243
244 // (G) Then collect conflicts between the first fragment and any nested
245 // fragments spread in the second fragment.
246 for _, innerFragmentName2 := range fieldsInfo2.fragmentNames {
247 conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, fragmentName1, innerFragmentName2)
248 }
249
250 // (G) Then collect conflicts between the second fragment and any nested
251 // fragments spread in the first fragment.
252 for _, innerFragmentName1 := range fieldsInfo1.fragmentNames {
253 conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, innerFragmentName1, fragmentName2)
254 }
255
256 return conflicts
257}
258
259// Find all conflicts found between two selection sets, including those found
260// via spreading in fragments. Called when determining if conflicts exist
261// between the sub-fields of two overlapping fields.
262func (rule *overlappingFieldsCanBeMergedRule) findConflictsBetweenSubSelectionSets(areMutuallyExclusive bool, parentType1 Named, selectionSet1 *ast.SelectionSet, parentType2 Named, selectionSet2 *ast.SelectionSet) []conflict {
263 conflicts := []conflict{}
264
265 fieldsInfo1 := rule.getFieldsAndFragmentNames(parentType1, selectionSet1)
266 fieldsInfo2 := rule.getFieldsAndFragmentNames(parentType2, selectionSet2)
267
268 // (H) First, collect all conflicts between these two collections of field.
269 conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo1, fieldsInfo2)
270
271 // (I) Then collect conflicts between the first collection of fields and
272 // those referenced by each fragment name associated with the second.
273 for _, fragmentName2 := range fieldsInfo2.fragmentNames {
274 conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo1, fragmentName2)
275 }
276
277 // (I) Then collect conflicts between the second collection of fields and
278 // those referenced by each fragment name associated with the first.
279 for _, fragmentName1 := range fieldsInfo1.fragmentNames {
280 conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo2, fragmentName1)
281 }
282
283 // (J) Also collect conflicts between any fragment names by the first and
284 // fragment names by the second. This compares each item in the first set of
285 // names to each item in the second set of names.
286 for _, fragmentName1 := range fieldsInfo1.fragmentNames {
287 for _, fragmentName2 := range fieldsInfo2.fragmentNames {
288 conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, fragmentName1, fragmentName2)
289 }
290 }
291 return conflicts
292}
293
294// Collect all Conflicts "within" one collection of fields.
295func (rule *overlappingFieldsCanBeMergedRule) collectConflictsWithin(conflicts []conflict, fieldsInfo *fieldsAndFragmentNames) []conflict {
296 // A field map is a keyed collection, where each key represents a response
297 // name and the value at that key is a list of all fields which provide that
298 // response name. For every response name, if there are multiple fields, they
299 // must be compared to find a potential conflict.
300 for _, responseName := range fieldsInfo.fieldsOrder {
301 fields, ok := fieldsInfo.fieldMap[responseName]
302 if !ok {
303 continue
304 }
305 // This compares every field in the list to every other field in this list
306 // (except to itself). If the list only has one item, nothing needs to
307 // be compared.
308 if len(fields) <= 1 {
309 continue
310 }
311 for i := 0; i < len(fields); i++ {
312 for k := i + 1; k < len(fields); k++ {
313 // within one collection is never mutually exclusive
314 isMutuallyExclusive := false
315 conflict := rule.findConflict(isMutuallyExclusive, responseName, fields[i], fields[k])
316 if conflict != nil {
317 conflicts = append(conflicts, *conflict)
318 }
319 }
320 }
321 }
322 return conflicts
323}
324
325// Collect all Conflicts between two collections of fields. This is similar to,
326// but different from the `collectConflictsWithin` function above. This check
327// assumes that `collectConflictsWithin` has already been called on each
328// provided collection of fields. This is true because this validator traverses
329// each individual selection set.
330func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetween(conflicts []conflict, parentFieldsAreMutuallyExclusive bool,
331 fieldsInfo1 *fieldsAndFragmentNames,
332 fieldsInfo2 *fieldsAndFragmentNames) []conflict {
333 // A field map is a keyed collection, where each key represents a response
334 // name and the value at that key is a list of all fields which provide that
335 // response name. For any response name which appears in both provided field
336 // maps, each field from the first field map must be compared to every field
337 // in the second field map to find potential conflicts.
338 for _, responseName := range fieldsInfo1.fieldsOrder {
339 fields1, ok1 := fieldsInfo1.fieldMap[responseName]
340 fields2, ok2 := fieldsInfo2.fieldMap[responseName]
341 if !ok1 || !ok2 {
342 continue
343 }
344 for i := 0; i < len(fields1); i++ {
345 for k := 0; k < len(fields2); k++ {
346 conflict := rule.findConflict(parentFieldsAreMutuallyExclusive, responseName, fields1[i], fields2[k])
347 if conflict != nil {
348 conflicts = append(conflicts, *conflict)
349 }
350 }
351 }
352 }
353 return conflicts
354}
355
356// findConflict Determines if there is a conflict between two particular fields.
357func (rule *overlappingFieldsCanBeMergedRule) findConflict(parentFieldsAreMutuallyExclusive bool, responseName string, field *fieldDefPair, field2 *fieldDefPair) *conflict {
358
359 parentType1 := field.ParentType
360 ast1 := field.Field
361 def1 := field.FieldDef
362
363 parentType2 := field2.ParentType
364 ast2 := field2.Field
365 def2 := field2.FieldDef
366
367 // If it is known that two fields could not possibly apply at the same
368 // time, due to the parent types, then it is safe to permit them to diverge
369 // in aliased field or arguments used as they will not present any ambiguity
370 // by differing.
371 // It is known that two parent types could never overlap if they are
372 // different Object types. Interface or Union types might overlap - if not
373 // in the current state of the schema, then perhaps in some future version,
374 // thus may not safely diverge.
375 _, isParentType1Object := parentType1.(*Object)
376 _, isParentType2Object := parentType2.(*Object)
377 areMutuallyExclusive := parentFieldsAreMutuallyExclusive || parentType1 != parentType2 && isParentType1Object && isParentType2Object
378
379 // The return type for each field.
380 var type1 Type
381 var type2 Type
382 if def1 != nil {
383 type1 = def1.Type
384 }
385 if def2 != nil {
386 type2 = def2.Type
387 }
388
389 if !areMutuallyExclusive {
390 // Two aliases must refer to the same field.
391 name1 := ""
392 name2 := ""
393
394 if ast1.Name != nil {
395 name1 = ast1.Name.Value
396 }
397 if ast2.Name != nil {
398 name2 = ast2.Name.Value
399 }
400 if name1 != name2 {
401 return &conflict{
402 Reason: conflictReason{
403 Name: responseName,
404 Message: fmt.Sprintf(`%v and %v are different fields`, name1, name2),
405 },
406 FieldsLeft: []ast.Node{ast1},
407 FieldsRight: []ast.Node{ast2},
408 }
409 }
410
411 // Two field calls must have the same arguments.
412 if !sameArguments(ast1.Arguments, ast2.Arguments) {
413 return &conflict{
414 Reason: conflictReason{
415 Name: responseName,
416 Message: `they have differing arguments`,
417 },
418 FieldsLeft: []ast.Node{ast1},
419 FieldsRight: []ast.Node{ast2},
420 }
421 }
422 }
423
424 if type1 != nil && type2 != nil && doTypesConflict(type1, type2) {
425 return &conflict{
426 Reason: conflictReason{
427 Name: responseName,
428 Message: fmt.Sprintf(`they return conflicting types %v and %v`, type1, type2),
429 },
430 FieldsLeft: []ast.Node{ast1},
431 FieldsRight: []ast.Node{ast2},
432 }
433 }
434
435 // Collect and compare sub-fields. Use the same "visited fragment names" list
436 // for both collections so fields in a fragment reference are never
437 // compared to themselves.
438 selectionSet1 := ast1.SelectionSet
439 selectionSet2 := ast2.SelectionSet
440 if selectionSet1 != nil && selectionSet2 != nil {
441 conflicts := rule.findConflictsBetweenSubSelectionSets(areMutuallyExclusive, GetNamed(type1), selectionSet1, GetNamed(type2), selectionSet2)
442 return subfieldConflicts(conflicts, responseName, ast1, ast2)
443 }
444 return nil
445}
446
447// Given a selection set, return the collection of fields (a mapping of response
448// name to field ASTs and definitions) as well as a list of fragment names
449// referenced via fragment spreads.
450func (rule *overlappingFieldsCanBeMergedRule) getFieldsAndFragmentNames(parentType Named, selectionSet *ast.SelectionSet) *fieldsAndFragmentNames {
451 if cached, ok := rule.cacheMap[selectionSet]; ok && cached != nil {
452 return cached
453 }
454
455 astAndDefs := astAndDefCollection{}
456 fieldsOrder := []string{}
457 fragmentNames := []string{}
458 fragmentNamesMap := map[string]bool{}
459
460 var collectFieldsAndFragmentNames func(parentType Named, selectionSet *ast.SelectionSet)
461 collectFieldsAndFragmentNames = func(parentType Named, selectionSet *ast.SelectionSet) {
462 for _, selection := range selectionSet.Selections {
463 switch selection := selection.(type) {
464 case *ast.Field:
465 fieldName := ""
466 if selection.Name != nil {
467 fieldName = selection.Name.Value
468 }
469 var fieldDef *FieldDefinition
470 if parentType, ok := parentType.(*Object); ok && parentType != nil {
471 fieldDef, _ = parentType.Fields()[fieldName]
472 }
473 if parentType, ok := parentType.(*Interface); ok && parentType != nil {
474 fieldDef, _ = parentType.Fields()[fieldName]
475 }
476
477 responseName := fieldName
478 if selection.Alias != nil {
479 responseName = selection.Alias.Value
480 }
481
482 fieldDefPairs, ok := astAndDefs[responseName]
483 if !ok || fieldDefPairs == nil {
484 fieldDefPairs = []*fieldDefPair{}
485 fieldsOrder = append(fieldsOrder, responseName)
486 }
487
488 fieldDefPairs = append(fieldDefPairs, &fieldDefPair{
489 ParentType: parentType,
490 Field: selection,
491 FieldDef: fieldDef,
492 })
493 astAndDefs[responseName] = fieldDefPairs
494 case *ast.FragmentSpread:
495 fieldName := ""
496 if selection.Name != nil {
497 fieldName = selection.Name.Value
498 }
499 if val, ok := fragmentNamesMap[fieldName]; !ok || !val {
500 fragmentNamesMap[fieldName] = true
501 fragmentNames = append(fragmentNames, fieldName)
502 }
503 case *ast.InlineFragment:
504 typeCondition := selection.TypeCondition
505 inlineFragmentType := parentType
506 if typeCondition != nil {
507 ttype, err := typeFromAST(*(rule.context.Schema()), typeCondition)
508 if err == nil {
509 inlineFragmentType, _ = ttype.(Named)
510 }
511 }
512 collectFieldsAndFragmentNames(inlineFragmentType, selection.SelectionSet)
513 }
514 }
515 }
516 collectFieldsAndFragmentNames(parentType, selectionSet)
517
518 cached := &fieldsAndFragmentNames{
519 fieldMap: astAndDefs,
520 fieldsOrder: fieldsOrder,
521 fragmentNames: fragmentNames,
522 }
523
524 rule.cacheMap[selectionSet] = cached
525 return cached
526}
527
528func (rule *overlappingFieldsCanBeMergedRule) getReferencedFieldsAndFragmentNames(fragment *ast.FragmentDefinition) *fieldsAndFragmentNames {
529 // Short-circuit building a type from the AST if possible.
530 if cached, ok := rule.cacheMap[fragment.SelectionSet]; ok && cached != nil {
531 return cached
532 }
533 fragmentType, err := typeFromAST(*(rule.context.Schema()), fragment.TypeCondition)
534 if err != nil {
535 return nil
536 }
537 return rule.getFieldsAndFragmentNames(fragmentType, fragment.SelectionSet)
538}
539
540type conflictReason struct {
541 Name string
542 Message interface{} // conflictReason || []conflictReason
543}
544type conflict struct {
545 Reason conflictReason
546 FieldsLeft []ast.Node
547 FieldsRight []ast.Node
548}
549
550// a.k.a AstAndDef
551type fieldDefPair struct {
552 ParentType Named
553 Field *ast.Field
554 FieldDef *FieldDefinition
555}
556type astAndDefCollection map[string][]*fieldDefPair
557
558// cache struct for fields, its order and fragments names
559type fieldsAndFragmentNames struct {
560 fieldMap astAndDefCollection
561 fieldsOrder []string // stores the order of field names in fieldMap
562 fragmentNames []string
563}
564
565// pairSet A way to keep track of pairs of things when the ordering of the pair does
566// not matter. We do this by maintaining a sort of double adjacency sets.
567type pairSet struct {
568 data map[string]map[string]bool
569}
570
571func newPairSet() *pairSet {
572 return &pairSet{
573 data: map[string]map[string]bool{},
574 }
575}
576func (pair *pairSet) Has(a string, b string, areMutuallyExclusive bool) bool {
577 first, ok := pair.data[a]
578 if !ok || first == nil {
579 return false
580 }
581 res, ok := first[b]
582 if !ok {
583 return false
584 }
585 // areMutuallyExclusive being false is a superset of being true,
586 // hence if we want to know if this PairSet "has" these two with no
587 // exclusivity, we have to ensure it was added as such.
588 if !areMutuallyExclusive {
589 return res == false
590 }
591 return true
592}
593func (pair *pairSet) Add(a string, b string, areMutuallyExclusive bool) {
594 pair.data = pairSetAdd(pair.data, a, b, areMutuallyExclusive)
595 pair.data = pairSetAdd(pair.data, b, a, areMutuallyExclusive)
596}
597func pairSetAdd(data map[string]map[string]bool, a, b string, areMutuallyExclusive bool) map[string]map[string]bool {
598 set, ok := data[a]
599 if !ok || set == nil {
600 set = map[string]bool{}
601 }
602 set[b] = areMutuallyExclusive
603 data[a] = set
604 return data
605}
606
607func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool {
608 if len(args1) != len(args2) {
609 return false
610 }
611
612 for _, arg1 := range args1 {
613 arg1Name := ""
614 if arg1.Name != nil {
615 arg1Name = arg1.Name.Value
616 }
617
618 var foundArgs2 *ast.Argument
619 for _, arg2 := range args2 {
620 arg2Name := ""
621 if arg2.Name != nil {
622 arg2Name = arg2.Name.Value
623 }
624 if arg1Name == arg2Name {
625 foundArgs2 = arg2
626 }
627 break
628 }
629 if foundArgs2 == nil {
630 return false
631 }
632 if sameValue(arg1.Value, foundArgs2.Value) == false {
633 return false
634 }
635 }
636
637 return true
638}
639
640func sameValue(value1 ast.Value, value2 ast.Value) bool {
641 if value1 == nil && value2 == nil {
642 return true
643 }
644 val1 := printer.Print(value1)
645 val2 := printer.Print(value2)
646
647 return val1 == val2
648}
649
650// Two types conflict if both types could not apply to a value simultaneously.
651// Composite types are ignored as their individual field types will be compared
652// later recursively. However List and Non-Null types must match.
653func doTypesConflict(type1 Output, type2 Output) bool {
654 if type1, ok := type1.(*List); ok {
655 if type2, ok := type2.(*List); ok {
656 return doTypesConflict(type1.OfType, type2.OfType)
657 }
658 return true
659 }
660 if type2, ok := type2.(*List); ok {
661 if type1, ok := type1.(*List); ok {
662 return doTypesConflict(type1.OfType, type2.OfType)
663 }
664 return true
665 }
666 if type1, ok := type1.(*NonNull); ok {
667 if type2, ok := type2.(*NonNull); ok {
668 return doTypesConflict(type1.OfType, type2.OfType)
669 }
670 return true
671 }
672 if type2, ok := type2.(*NonNull); ok {
673 if type1, ok := type1.(*NonNull); ok {
674 return doTypesConflict(type1.OfType, type2.OfType)
675 }
676 return true
677 }
678 if IsLeafType(type1) || IsLeafType(type2) {
679 return type1 != type2
680 }
681 return false
682}
683
684// subfieldConflicts Given a series of Conflicts which occurred between two sub-fields, generate a single Conflict.
685func subfieldConflicts(conflicts []conflict, responseName string, ast1 *ast.Field, ast2 *ast.Field) *conflict {
686 if len(conflicts) > 0 {
687 conflictReasons := []conflictReason{}
688 conflictFieldsLeft := []ast.Node{ast1}
689 conflictFieldsRight := []ast.Node{ast2}
690 for _, c := range conflicts {
691 conflictReasons = append(conflictReasons, c.Reason)
692 conflictFieldsLeft = append(conflictFieldsLeft, c.FieldsLeft...)
693 conflictFieldsRight = append(conflictFieldsRight, c.FieldsRight...)
694 }
695
696 return &conflict{
697 Reason: conflictReason{
698 Name: responseName,
699 Message: conflictReasons,
700 },
701 FieldsLeft: conflictFieldsLeft,
702 FieldsRight: conflictFieldsRight,
703 }
704 }
705 return nil
706}