findConflict function

Conflict? findConflict(
  1. ValidationCtx context,
  2. Map<SelectionSetNode, FieldsAndFragmentNames> cachedFieldsAndFragmentNames,
  3. PairSet comparedFragmentPairs,
  4. bool parentFieldsAreMutuallyExclusive,
  5. String responseName,
  6. NodeAndDef field1,
  7. 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);
  }
}