findConflict function
Conflict?
findConflict(
- ValidationCtx context,
- Map<
SelectionSetNode, FieldsAndFragmentNames> cachedFieldsAndFragmentNames, - PairSet comparedFragmentPairs,
- bool parentFieldsAreMutuallyExclusive,
- String responseName,
- NodeAndDef field1,
- NodeAndDef field2,
Determines if there is a conflict between two particular fields, including comparing their sub-fields.
Implementation
Conflict? findConflict(
ValidationCtx context,
Map<SelectionSetNode, FieldsAndFragmentNames> cachedFieldsAndFragmentNames,
PairSet comparedFragmentPairs,
bool parentFieldsAreMutuallyExclusive,
String responseName,
NodeAndDef field1,
NodeAndDef field2,
) {
final parentType1 = field1.parentType;
final parentType2 = field2.parentType;
// If it is known that two fields could not possibly apply at the same
// time, due to the parent types, then it is safe to permit them to diverge
// in aliased field or arguments used as they will not present any ambiguity
// by differing.
// It is known that two parent types could never overlap if they are
// different Object types. Interface or Union types might overlap - if not
// in the current state of the schema, then perhaps in some future version,
// thus may not safely diverge.
final areMutuallyExclusive = parentFieldsAreMutuallyExclusive ||
(parentType1 != parentType2 &&
parentType1 is GraphQLObjectType &&
!parentType1.isInterface &&
parentType2 is GraphQLObjectType &&
!parentType2.isInterface);
if (!areMutuallyExclusive) {
// Two aliases must refer to the same field.
final name1 = field1.node.name.value;
final name2 = field2.node.name.value;
if (name1 != name2) {
return Conflict(
ConflictReason(
responseName,
[],
'"${name1}" and "${name2}" are different fields',
),
[field1.node],
[field2.node],
);
}
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
final args1 = field1.node.arguments;
// istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
final args2 = field2.node.arguments;
// Two field calls must have the same arguments.
if (!sameArguments(args1, args2)) {
return Conflict(
ConflictReason(responseName, [], 'they have differing arguments'),
[field1.node],
[field2.node],
);
}
}
// The return type for each field.
final type1 = field1.def?.type;
final type2 = field2.def?.type;
if (type1 != null && type2 != null && doTypesConflict(type1, type2)) {
return Conflict(
ConflictReason(
responseName,
[],
'they return conflicting types "${inspect(type1)}" and "${inspect(
type2,
)}"',
),
[field1.node],
[field2.node],
);
}
// Collect and compare sub-fields. Use the same "visited fragment names" list
// for both collections so fields in a fragment reference are never
// compared to themselves.
final selectionSet1 = field1.node.selectionSet;
final selectionSet2 = field2.node.selectionSet;
if (selectionSet1 != null && selectionSet2 != null) {
final conflicts = findConflictsBetweenSubSelectionSets(
context,
cachedFieldsAndFragmentNames,
comparedFragmentPairs,
areMutuallyExclusive,
type1 == null ? null : getNamedType(type1),
selectionSet1,
type2 == null ? null : getNamedType(type2),
selectionSet2,
);
return subfieldConflicts(conflicts, responseName, field1.node, field2.node);
}
}