private bool SameArguments( FieldDefPair fieldDefPair1, FieldDefPair fieldDefPair2) { var arguments1 = fieldDefPair1.Field.Arguments? .ToDictionary(l => l.Name, l => l); var arguments2 = fieldDefPair2.Field.Arguments? .ToDictionary(l => l.Name, l => l); if (arguments1 == null && arguments2 == null) { return(true); } if (arguments1 == null) { return(false); } if (arguments2 == null) { return(false); } if (arguments1.Count() != arguments2.Count()) { return(false); } return(arguments1.All(arg1 => { if (!arguments2.ContainsKey(arg1.Key)) { return false; } var arg2 = arguments2[arg1.Key]; var value1 = Arguments.CoerceArgumentValue( _context.Schema, _context.VariableValues, arg1.Key, fieldDefPair1.FieldDef.GetArgument(arg1.Key), arg1.Value); var value2 = Arguments.CoerceArgumentValue( _context.Schema, _context.VariableValues, arg1.Key, fieldDefPair2.FieldDef.GetArgument(arg1.Key), arg2); return SameValue(value1, value2); })); }
private Conflict FindConflict( ValidationContext context, Dictionary <SelectionSet, CachedField> cachedFieldsAndFragmentNames, PairSet comparedFragmentPairs, bool parentFieldsAreMutuallyExclusive, string responseName, FieldDefPair fieldDefPair1, FieldDefPair fieldDefPair2) { var parentType1 = fieldDefPair1.ParentType; var node1 = fieldDefPair1.Field; var def1 = fieldDefPair1.FieldDef; var parentType2 = fieldDefPair2.ParentType; var node2 = fieldDefPair2.Field; var def2 = fieldDefPair2.FieldDef; // 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. var areMutuallyExclusive = parentFieldsAreMutuallyExclusive || parentType1 != parentType2 && isObjectType(parentType1) && isObjectType(parentType2); // return type for each field. var type1 = def1?.ResolvedType; var type2 = def2?.ResolvedType; if (!areMutuallyExclusive) { // Two aliases must refer to the same field. var name1 = node1.GetName(); var name2 = node2.GetName(); if (name1 != name2) { return(new Conflict { Reason = new ConflictReason { Name = responseName, Message = new Message { Msg = $"{name1} and {name2} are different fields" } }, FieldsLeft = new List <ISelection> { node1 }, FieldsRight = new List <ISelection> { node2 } }); } // Two field calls must have the same arguments. if (!SameArguments(node1.GetArguments(), node2.GetArguments())) { return(new Conflict { Reason = new ConflictReason { Name = responseName, Message = new Message { Msg = "they have differing arguments" } }, FieldsLeft = new List <ISelection> { node1 }, FieldsRight = new List <ISelection> { node2 } }); } } if (type1 != null && type2 != null && DoTypesConflict(type1, type2)) { return(new Conflict { Reason = new ConflictReason { Name = responseName, Message = new Message { Msg = $"they return conflicting types {type1} and {type2}" } }, FieldsLeft = new List <ISelection> { node1 }, FieldsRight = new List <ISelection> { node2 } }); } // 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. var selectionSet1 = node1.GetSelectionSet(); var selectionSet2 = node2.GetSelectionSet(); if (selectionSet1 != null && selectionSet2 != null) { var conflicts = FindConflictsBetweenSubSelectionSets( context, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, type1.GetNamedType(), selectionSet1, type2.GetNamedType(), selectionSet2); return(SubfieldConflicts(conflicts, responseName, node1, node2)); } return(null); }