Skip to content

Bug Fix: Rule Validation Failure with multiple fragments and unknown Graph Types #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,22 @@ private bool CompareAllFields(
if (fields.Count < 2)
return true;

// must compare every field to every other field
// inside the field set O(n^2) time :(
// take a short cut if some fields are not in a state
// that warrants complete validation
for (var i = 0; i < fields.Count - 1; i++)
{
if (!this.ShouldBeValidatedForRule(fields[i]))
continue;

for (var j = i + 1; j < fields.Count; j++)
{
// don't attempt to perform a validation unless both
// fields are correct and validatable
if (!this.ShouldBeValidatedForRule(fields[j]))
continue;

var passedValidation = this.ValidateFieldPair(
context,
ownerField,
Expand Down Expand Up @@ -226,6 +238,21 @@ private bool AreSameShape(IFieldDocumentPart leftField, IFieldDocumentPart right
return true;
}

/// <summary>
/// Determines whether the field document part should be validated against this rule. Field
/// document parts may not qualify for validation if other errors may exist within them.
/// </summary>
/// <param name="fieldToCheck">The field to check.</param>
/// <returns><c>true</c> if the document parts can be checked for co-existance; otherwise, <c>false</c>.</returns>
private bool ShouldBeValidatedForRule(IFieldDocumentPart fieldToCheck)
{
IGraphType graphType = null;
if (fieldToCheck.Parent is IFieldSelectionSetDocumentPart fsdl)
graphType = fsdl.GraphType;

return graphType != null;
}

/// <summary>
/// Inspects both fields to see if any target graph type restrctions exist (such as with fragmetn spreads)
/// such that the fields could not be included in the same object resolution. For example if the existing field targets a
Expand All @@ -246,7 +273,7 @@ private bool CanCoExist(ISchema targetSchema, IFieldDocumentPart leftField, IFie
if (rightField.Parent is IFieldSelectionSetDocumentPart fsdr)
rightSourceGraphType = fsdr.GraphType;

// neither should be null at this point
// last ditch safety check: neither should be null at this point.
if (leftSourceGraphType == null)
{
throw new GraphExecutionException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public override bool Execute(DocumentValidationContext context)
{
this.ValidationError(
context,
$"No known graph type was found for the target fragment.");
$"No known graph type was found for the target fragment (Target: '{fragment.TargetGraphTypeName}').");

return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,24 @@ public class DocumentValidationRuleProcessorTests
{
public static readonly List<object> QueriesToFail;
public static readonly List<object> QueriesToPass;
private static bool _rejectFurtherQueries;

private static void AddQueryFailure(string ruleNumberToBreak, string query)
private static void AddQueryFailure(string ruleNumberToBreak, string query, bool isolateAndStopOthers = false)
{
if (_rejectFurtherQueries)
return;

if (isolateAndStopOthers)
QueriesToFail.Clear();

QueriesToFail.Add(new object[] { ruleNumberToBreak, query });
_rejectFurtherQueries = _rejectFurtherQueries || isolateAndStopOthers;
}

private static void AddQuerySuccess(string relatedRule, string query)
{
// relatedRule is not used added to make it clear what is being tested against
// when setting up tests
QueriesToPass.Add(new object[] { query });
}

Expand Down Expand Up @@ -154,6 +164,14 @@ fragment ElevFragment on Elevator {
// inlined fragments must have a declared target type (invalid type bob)
AddQueryFailure("5.5.1.2", "query Operation1{ peopleMover(id: 5) { ... on Bob { id } } } ");

// inlined fragments must have a declared target type of the correct case(invalid case on Escalator)
AddQueryFailure("5.5.1.2", "query Operation1{ peopleMover(id: 5) { ... on EscalaTor { id name } } } ");

// inlined fragments must have a declared target type of the correct case(invalid case on Escalator)
// special test for multiple inline fragments that may result in a need to merge
// this should trigger on fragment type checking (5.5.1.2), not on fragment mergability (5.3.2)
AddQueryFailure("5.5.1.2", "query Operation1{ peopleMover(id: 5) { ... on Elevator {id name} ... on EscalaTor { id name } } } ");

// all declared target types of a fragment must be Union, interface or object on named fragment
// frag 1 declars a target of the string scalar
AddQueryFailure("5.5.1.3", "query Operation1{ peopleMovers { elevator(id: 5){ ...frag1 } } } fragment frag1 on String { unknownField1 }");
Expand Down Expand Up @@ -368,6 +386,8 @@ public void ExecuteRule_EnsureCorrectErrorIsGenerated(string expectedRuleError,

var ruleBroke = message.MetaData["Rule"];
Assert.AreEqual(expectedRuleError, ruleBroke.ToString());
if (_rejectFurtherQueries)
Assert.Inconclusive("Query executed in isolation. cannot determine status of others");
}

[TestCaseSource(nameof(QueriesToPass))]
Expand Down