diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/InlineFragmentOperationRewriter.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/InlineFragmentOperationRewriter.cs index f7ac20e75e8..d10ecb8dac7 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/InlineFragmentOperationRewriter.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/InlineFragmentOperationRewriter.cs @@ -4,6 +4,7 @@ namespace HotChocolate.Fusion.Planning; +// TODO: We need to merge selections public sealed class InlineFragmentOperationRewriter(CompositeSchema schema) { public DocumentNode RewriteDocument(DocumentNode document, string? operationName) diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/Nodes/InlineFragmentPlanNode.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/Nodes/InlineFragmentPlanNode.cs index 72c89cc6217..02b39d6d9f3 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/Nodes/InlineFragmentPlanNode.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/Nodes/InlineFragmentPlanNode.cs @@ -8,14 +8,15 @@ public sealed class InlineFragmentPlanNode : SelectionPlanNode public InlineFragmentPlanNode( ICompositeNamedType declaringType, InlineFragmentNode inlineFragment) - : this(declaringType, inlineFragment.SelectionSet.Selections) + : this(declaringType, inlineFragment.Directives, inlineFragment.SelectionSet.Selections) { } public InlineFragmentPlanNode( ICompositeNamedType declaringType, + IReadOnlyList directiveNodes, IReadOnlyList selectionNodes) - : base(declaringType, [], selectionNodes) + : base(declaringType, directiveNodes, selectionNodes) { } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationPlanner.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationPlanner.cs index e8d0ab60417..d1ea10d458e 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationPlanner.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationPlanner.cs @@ -8,6 +8,8 @@ namespace HotChocolate.Fusion.Planning; +// TODO: Flatten unnecessary inline fragments +// TODO: Remove selections from skipped fragment if they are part of the parent selection public sealed class OperationPlanner(CompositeSchema schema) { private int _lastRequirementId; @@ -51,7 +53,7 @@ private bool TryPlanSelectionSet( "A leaf field cannot be a parent node."); } - List? unresolvedFields = null; + List? unresolvedSelections = null; // List? unresolvedTypes = null; var type = (CompositeComplexType)context.Parent.DeclaringType; var haveConditionalSelectionsBeenRemoved = false; @@ -68,10 +70,10 @@ private bool TryPlanSelectionSet( context, type, selection, - unresolvedField => + unresolvedSelection => { - unresolvedFields ??= new List(); - unresolvedFields.Add(unresolvedField); + unresolvedSelections ??= new List(); + unresolvedSelections.Add(unresolvedSelection); }); } @@ -96,16 +98,16 @@ private bool TryPlanSelectionSet( } return skipUnresolved - || unresolvedFields is null - || unresolvedFields.Count == 0 - || TryHandleUnresolvedSelections(context, type, unresolvedFields); + || unresolvedSelections is null + || unresolvedSelections.Count == 0 + || TryHandleUnresolvedSelections(context, type, unresolvedSelections); } private bool TryPlanSelection( PlaningContext context, CompositeComplexType type, ISelectionNode selectionNode, - Action trackUnresolvedField) + Action trackUnresolvedSelection) { if (selectionNode is FieldNode fieldNode) { @@ -113,7 +115,73 @@ private bool TryPlanSelection( context, type, fieldNode, - trackUnresolvedField); + trackUnresolvedSelection); + } + + if (selectionNode is InlineFragmentNode inlineFragmentNode) + { + return TryPlanInlineFragmentSelection( + context, + type, + inlineFragmentNode, + trackUnresolvedSelection); + } + + return false; + } + + private bool TryPlanInlineFragmentSelection( + PlaningContext context, + CompositeComplexType type, + InlineFragmentNode inlineFragmentNode, + Action trackUnresolvedSelection) + { + var typeCondition = type; + if (inlineFragmentNode.TypeCondition?.Name.Value is { } conditionTypeName && + // TODO: CompositeComplexType does not include unions which are a valid value for type conditions. + schema.TryGetType(conditionTypeName, out var typeConditionType)) + { + typeCondition = typeConditionType; + } + + var inlineFragmentPlanNode = new InlineFragmentPlanNode(typeCondition, inlineFragmentNode); + var inlineFragmentContext = new PlaningContext(context.Operation, inlineFragmentPlanNode, + ImmutableStack.Empty); + List? unresolvedSelections = null; + + foreach (var selection in inlineFragmentNode.SelectionSet.Selections) + { + if (IsSelectionAlwaysSkipped(selection)) + { + continue; + } + + TryPlanSelection( + inlineFragmentContext, + typeCondition, + selection, + unresolvedSelection => + { + unresolvedSelections ??= new List(); + unresolvedSelections.Add(unresolvedSelection); + }); + } + + if (unresolvedSelections is { Count: > 0 }) + { + var unresolvedInlineFragment = + new UnresolvedInlineFragment(inlineFragmentNode.Directives, typeCondition, unresolvedSelections); + + trackUnresolvedSelection(unresolvedInlineFragment); + } + + if (inlineFragmentPlanNode.Selections.Count > 0) + { + AddSelectionDirectives(inlineFragmentPlanNode, inlineFragmentNode.Directives); + + context.Parent.AddSelection(inlineFragmentPlanNode); + + return true; } return false; @@ -123,7 +191,7 @@ private bool TryPlanFieldSelection( PlaningContext context, CompositeComplexType type, FieldNode fieldNode, - Action trackUnresolvedField) + Action trackUnresolvedSelection) { if (!type.Fields.TryGetField(fieldNode.Name.Value, out var field)) { @@ -134,8 +202,7 @@ private bool TryPlanFieldSelection( // if we have an operation plan node we have a pre-validated set of // root fields, so we now the field will be resolvable on the // source schema. - if (context.Parent is OperationPlanNode - || IsResolvable(fieldNode, field, context.Operation.SchemaName)) + if (context.Parent is OperationPlanNode || IsResolvable(fieldNode, field, context.Operation.SchemaName)) { var fieldNamedType = field.Type.NamedType(); @@ -178,12 +245,12 @@ private bool TryPlanFieldSelection( return true; } - trackUnresolvedField(new UnresolvedField(fieldNode, field)); + trackUnresolvedSelection(new UnresolvedField(fieldNode, field)); return false; } // unresolvable fields will be collected to backtrack later. - trackUnresolvedField(new UnresolvedField(fieldNode, field)); + trackUnresolvedSelection(new UnresolvedField(fieldNode, field)); return false; } @@ -195,8 +262,8 @@ private void AddSelectionDirectives( { var directiveType = schema.GetDirectiveType(directiveNode.Name.Value); - if ((directiveType == schema.SkipDirective || directiveType == schema.IncludeDirective) - && directiveNode.Arguments[0].Value is BooleanValueNode) + if ((directiveType == schema.SkipDirective || directiveType == schema.IncludeDirective) && + directiveNode.Arguments[0].Value is BooleanValueNode) { continue; } @@ -210,7 +277,7 @@ private void AddSelectionDirectives( private bool TryHandleUnresolvedSelections( PlaningContext context, CompositeComplexType type, - List unresolved) + List unresolvedSelections) { if (!TryResolveEntityType(context.Parent, out var entityPath)) { @@ -221,13 +288,13 @@ private bool TryHandleUnresolvedSelections( // if any of the unresolved selections can be resolved through one of the entity lookups. var schemasInContext = new Dictionary(); var processedFields = new HashSet(); - var fields = new List(); + var selections = new List(); schemasInContext.Add(context.Operation.SchemaName, context.Operation); // we first try to weight the schemas that the fields can be resolved by. // The schema is weighted by the fields it potentially can resolve. - var schemasWeighted = GetSchemasWeighted(unresolved, schemasInContext.Keys); + var schemasWeighted = GetSchemasWeighted(unresolvedSelections, schemasInContext.Keys); foreach (var schemaName in schemasWeighted.OrderByDescending(t => t.Value).Select(t => t.Key)) { @@ -256,19 +323,48 @@ private bool TryHandleUnresolvedSelections( // note : this can lead to a operation explosions as fields could be unresolvable // and would be spread out in the lower level call. We do that for now to test out the // overall concept and will backtrack later to the upper call. - fields.Clear(); + selections.Clear(); - foreach (var unresolvedField in unresolved) + foreach (var unresolvedSelection in unresolvedSelections) { - if (unresolvedField.Field.Sources.ContainsSchema(schemaName) - && !processedFields.Contains(unresolvedField.Field.Name)) + if (unresolvedSelection is UnresolvedField unresolvedField) { - fields.Add(unresolvedField.FieldNode); + if (unresolvedField.Field.Sources.ContainsSchema(schemaName) && + !processedFields.Contains(unresolvedField.Field.Name)) + { + selections.Add(unresolvedField.FieldNode); + } + } + // TODO: Are we only concerned with the top-level of fields here? + else if (unresolvedSelection is UnresolvedInlineFragment unresolvedInlineFragment) + { + var resolvableFields = new List(); + + foreach (var unresolvedSubSelection in unresolvedInlineFragment.UnresolvedSelections) + { + if (unresolvedSubSelection is UnresolvedField unresolvedSubField) + { + // We're specifically not checking processed fields here as fields outside the inline fragment should still be added. + if (unresolvedSubField.Field.Sources.ContainsSchema(schemaName)) + { + resolvableFields.Add(unresolvedSubField.FieldNode); + } + } + } + + if (resolvableFields.Count > 0) + { + selections.Add(new InlineFragmentNode( + null, + new NamedTypeNode(unresolvedInlineFragment.TypeCondition.Name), + unresolvedInlineFragment.Directives, + new SelectionSetNode(resolvableFields))); + } } } var (lookupOperation, lookupField) = - CreateLookupOperation(schemaName, lookup, type, context.Parent, fields); + CreateLookupOperation(schemaName, lookup, type, context.Parent, selections); if (!TryPlanSelectionSet(context with { Operation = lookupOperation, Parent = lookupField }, true)) { continue; @@ -338,13 +434,43 @@ private bool TryHandleUnresolvedSelections( processedFields.Add(field.Field.Name); break; + case InlineFragmentPlanNode inlineFragmentNode: + foreach (var inlineFragmentSelection in inlineFragmentNode.Selections) + { + if (inlineFragmentSelection is FieldPlanNode field) + { + processedFields.Add(field.Field.Name); + } + } + + break; + default: throw new NotSupportedException(); } } } - return unresolved.Count == processedFields.Count; + var unresolvedFields = new HashSet(); + foreach (var unresolvedSelection in unresolvedSelections) + { + if (unresolvedSelection is UnresolvedField unresolvedField) + { + unresolvedFields.Add(unresolvedField.Field.Name); + } + else if (unresolvedSelection is UnresolvedInlineFragment unresolvedInlineFragment) + { + foreach (var inlineFragmentSelection in unresolvedInlineFragment.UnresolvedSelections) + { + if (inlineFragmentSelection is UnresolvedField field) + { + unresolvedFields.Add(field.Field.Name); + } + } + } + } + + return unresolvedFields.Count == processedFields.Count; } /// @@ -414,8 +540,7 @@ private static bool TryGetLookup( var declaringType = (CompositeComplexType)selection.DeclaringType; var builder = ImmutableDictionary.CreateBuilder(); - if (declaringType.Sources.TryGetType(schemaName, out var source) - && source.Lookups.Length > 0) + if (declaringType.Sources.TryGetType(schemaName, out var source) && source.Lookups.Length > 0) { foreach (var possibleLookup in source.Lookups.OrderBy(t => t.Fields.Length)) { @@ -517,22 +642,33 @@ private LookupOperation CreateLookupOperation( } private static Dictionary GetSchemasWeighted( - IEnumerable unresolvedFields, + IEnumerable unresolvedSelections, IEnumerable skipSchemaNames) { var counts = new Dictionary(); + var unresolvedSelectionBacklog = new Queue(unresolvedSelections); - foreach (var unresolvedField in unresolvedFields) + while (unresolvedSelectionBacklog.TryDequeue(out var unresolvedSelection)) { - foreach (var schemaName in unresolvedField.Field.Sources.Schemas) + if (unresolvedSelection is UnresolvedField unresolvedField) { - if (counts.TryGetValue(schemaName, out var count)) + foreach (var schemaName in unresolvedField.Field.Sources.Schemas) { - counts[schemaName] = count + 1; + if (counts.TryGetValue(schemaName, out var count)) + { + counts[schemaName] = count + 1; + } + else + { + counts[schemaName] = 1; + } } - else + } + else if (unresolvedSelection is UnresolvedInlineFragment unresolvedInlineFragment) + { + foreach (var selection in unresolvedInlineFragment.UnresolvedSelections) { - counts[schemaName] = 1; + unresolvedSelectionBacklog.Enqueue(selection); } } } @@ -550,8 +686,10 @@ private static Dictionary GetSchemasWeighted( SelectionSetNode selectionSet) { var counts = new Dictionary(); + var selectionBacklog = new Queue(selectionSet.Selections); + var visitedSelections = new HashSet(SyntaxComparer.BySyntax); - foreach (var selectionNode in selectionSet.Selections) + while (selectionBacklog.TryDequeue(out var selectionNode)) { if (selectionNode is FieldNode fieldNode) { @@ -569,6 +707,16 @@ private static Dictionary GetSchemasWeighted( } } } + else if (selectionNode is InlineFragmentNode inlineFragmentNode) + { + foreach (var selection in inlineFragmentNode.SelectionSet.Selections) + { + if (visitedSelections.Add(selection)) + { + selectionBacklog.Enqueue(selection); + } + } + } } return counts; @@ -645,8 +793,7 @@ private void TryMakeOperationConditional( remove.AddRange( selection.Directives.Where( - t => t.Type == schema.SkipDirective - || t.Type == schema.IncludeDirective)); + t => t.Type == schema.SkipDirective || t.Type == schema.IncludeDirective)); foreach (var directive in remove) { @@ -687,19 +834,23 @@ private static bool IsSelectionAlwaysSkipped(ISelectionNode selectionNode) return false; } + // TODO: Needs to be scoped on operation unless planner is transient private string GetNextRequirementName() => $"__fusion_requirement_{++_lastRequirementId}"; public record SelectionPathSegment( SelectionPlanNode PlanNode); + public interface IUnresolvedSelection; + public record UnresolvedField( FieldNode FieldNode, - CompositeOutputField Field); + CompositeOutputField Field) : IUnresolvedSelection; - public record UnresolvedType( - InlineFragmentNode InlineFragment, - CompositeComplexType TypeCondition); + public record UnresolvedInlineFragment( + IReadOnlyList Directives, + CompositeComplexType TypeCondition, + List UnresolvedSelections) : IUnresolvedSelection; private record struct LookupOperation( OperationPlanNode Operation, diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationVariableBinder.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationVariableBinder.cs index 239b70685f5..bcb64464f70 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationVariableBinder.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Planning/OperationVariableBinder.cs @@ -46,15 +46,15 @@ private static void CollectAndBindUsedVariables( usedVariables.Add(variable.Name.Value); } } + } - foreach (var directive in field.Directives) + foreach (var directive in node.Directives) + { + foreach (var argument in directive.Arguments) { - foreach (var argument in directive.Arguments) + if (argument.Value is VariableNode variable) { - if (argument.Value is VariableNode variable) - { - usedVariables.Add(variable.Name.Value); - } + usedVariables.Add(variable.Name.Value); } } } @@ -67,7 +67,7 @@ private static void CollectAndBindUsedVariables( foreach (var variable in usedVariables) { - if(variableDefinitions.TryGetValue(variable, out var variableDefinition)) + if (variableDefinitions.TryGetValue(variable, out var variableDefinition)) { operation.AddVariableDefinition(variableDefinition); } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/SkipAndIncludeTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/SkipAndIncludeTests.cs index 17b55975d5f..0e1cbb0af5a 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/SkipAndIncludeTests.cs +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/SkipAndIncludeTests.cs @@ -2,6 +2,7 @@ namespace HotChocolate.Fusion; +// TODO: Test shared skip selection with one selection having an include - should fail today incorrectly! public class SkipAndIncludeTests : FusionTestBase { [Test] diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/SkipFragmentTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/SkipFragmentTests.cs new file mode 100644 index 00000000000..3e432a7d4ba --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/SkipFragmentTests.cs @@ -0,0 +1,1100 @@ +using static HotChocolate.Language.Utf8GraphQLParser; + +namespace HotChocolate.Fusion; + +public class SkipFragmentTests : FusionTestBase +{ + [Test] + public void Skipped_Root_Selection_Same_Selection_Without_Skip_In_Fragment() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) @skip(if: $skip) { + name + } + ... QueryFragment + } + + fragment QueryFragment on Query { + productBySlug(slug: $slug) { + name + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Selection_Same_Skipped_Selection_In_Fragment() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) @skip(if: $skip) { + name + } + ... QueryFragment + } + + fragment QueryFragment on Query { + productBySlug(slug: $slug) @skip(if: $skip) { + name + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Selection_Same_Skipped_Selection_With_Different_Skip_In_Fragment() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip1: Boolean!, $skip2: Boolean!) { + productBySlug(slug: $slug) @skip(if: $skip1) { + name + } + ... QueryFragment + } + + fragment QueryFragment on Query { + productBySlug(slug: $slug) @skip(if: $skip2) { + name + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Fragment() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + ... QueryFragment @skip(if: $skip) + } + + fragment QueryFragment on Query { + productBySlug(slug: $slug) { + name + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Fragment_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + ... QueryFragment @skip(if: true) + } + + fragment QueryFragment on Query { + productBySlug(slug: $slug) { + name + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Same_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + ... QueryFragment1 @skip(if: $skip) + ... QueryFragment2 + } + + fragment QueryFragment1 on Query { + productBySlug(slug: $slug) { + name + } + } + + fragment QueryFragment2 on Query { + products { + nodes { + description + } + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Same_Subgraph_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + ... QueryFragment1 @skip(if: true) + ... QueryFragment2 + } + + fragment QueryFragment1 on Query { + productBySlug(slug: $slug) { + name + } + } + + fragment QueryFragment2 on Query { + products { + nodes { + description + } + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Not yet supported by the planner")] + public void Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Different_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + ... QueryFragment1 @skip(if: $skip) + ... QueryFragment2 + } + + fragment QueryFragment1 on Query { + productBySlug(slug: $slug) { + name + } + } + + fragment QueryFragment2 on Query { + viewer { + displayName + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Not yet supported by the planner")] + public void Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Different_Subgraph_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + ... QueryFragment1 @skip(if: true) + ... QueryFragment2 + } + + fragment QueryFragment1 on Query { + productBySlug(slug: $slug) { + name + } + } + + fragment QueryFragment2 on Query { + viewer { + displayName + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Not yet supported by the planner")] + public void Skipped_Root_Fragment_With_Selections_From_Two_Subgraphs() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + ... QueryFragment @skip(if: $skip) + } + + fragment QueryFragment on Query { + productBySlug(slug: $slug) { + name + } + viewer { + displayName + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Fragment_With_Selections_From_Two_Subgraphs_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + ... QueryFragment @skip(if: true) + } + + fragment QueryFragment on Query { + productBySlug(slug: $slug) { + name + } + viewer { + displayName + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Fragments_With_Same_Root_Selection_From_Same_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip1: Boolean!, $skip2: Boolean!) { + ... QueryFragment1 @skip(if: $skip1) + ... QueryFragment2 @skip(if: $skip2) + } + + fragment QueryFragment1 on Query { + productBySlug(slug: $slug) { + name + } + } + + fragment QueryFragment2 on Query { + productBySlug(slug: $slug) { + name + description + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Root_Fragments_With_Same_Root_Selection_From_Same_Subgraph_Same_Variable() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + ... QueryFragment1 @skip(if: $skip) + ... QueryFragment2 @skip(if: $skip) + } + + fragment QueryFragment1 on Query { + productBySlug(slug: $slug) { + name + } + } + + fragment QueryFragment2 on Query { + productBySlug(slug: $slug) { + name + description + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Not yet supported by the planner")] + public void Skipped_Root_Fragments_With_Different_Root_Selection_From_Different_Subgraphs() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip1: Boolean!, $skip2: Boolean!) { + ... QueryFragment1 @skip(if: $skip1) + ... QueryFragment2 @skip(if: $skip2) + } + + fragment QueryFragment1 on Query { + productBySlug(slug: $slug) { + name + } + } + + fragment QueryFragment2 on Query { + viewer { + displayName + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Not yet supported by the planner")] + public void Skipped_Root_Fragments_With_Different_Root_Selection_From_Different_Subgraphs_Same_Variable() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + ... QueryFragment1 @skip(if: $skip) + ... QueryFragment2 @skip(if: $skip) + } + + fragment QueryFragment1 on Query { + productBySlug(slug: $slug) { + name + } + } + + fragment QueryFragment2 on Query { + viewer { + displayName + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Not yet supported by the planner")] + public void Skipped_Root_Fragments_With_Shared_Viewer_Selection_And_Sub_Selections_From_Two_Subgraphs_Each() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($skip1: Boolean!, $skip2: Boolean!) { + ... QueryFragment1 @skip(if: $skip1) + ... QueryFragment2 @skip(if: $skip2) + } + + fragment QueryFragment1 on Query { + viewer { + displayName + reviews { + nodes { + body + } + } + } + } + + fragment QueryFragment2 on Query { + viewer { + reviews { + nodes { + body + } + } + displayName + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Not yet supported by the planner")] + public void Skipped_Root_Fragments_With_Shared_Viewer_Selection_And_Sub_Selections_From_Two_Subgraphs_Each_Same_Variable() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($skip: Boolean!) { + ... QueryFragment1 @skip(if: $skip) + ... QueryFragment2 @skip(if: $skip) + } + + fragment QueryFragment1 on Query { + viewer { + displayName + reviews { + nodes { + body + } + } + } + } + + fragment QueryFragment2 on Query { + viewer { + reviews { + nodes { + body + } + } + displayName + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Selection_Same_Selection_Without_Skip_In_Fragment() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + name + ... ProductFragment @skip(if: $skip) + } + } + + fragment ProductFragment on Product { + name + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Selection_Same_Skipped_Selection_In_Fragment() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + name @skip(if: $skip) + ... ProductFragment + } + } + + fragment ProductFragment on Product { + name @skip(if: $skip) + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Selection_Same_Skipped_Selection_With_Different_Skip_In_Fragment() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip1: Boolean!, $skip2: Boolean!) { + productBySlug(slug: $slug) { + name @skip(if: $skip1) + ... ProductFragment + } + } + + fragment ProductFragment on Product { + name @skip(if: $skip2) + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... ProductFragment @skip(if: $skip) + } + } + + fragment ProductFragment on Product { + name + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + productBySlug(slug: $slug) { + ... ProductFragment @skip(if: true) + } + } + + fragment ProductFragment on Product { + name + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... ProductFragment1 @skip(if: $skip) + ... ProductFragment2 + } + } + + fragment ProductFragment1 on Product { + name + } + + fragment ProductFragment2 on Product { + description + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + productBySlug(slug: $slug) { + ... ProductFragment1 @skip(if: true) + ... ProductFragment2 + } + } + + fragment ProductFragment1 on Product { + name + } + + fragment ProductFragment2 on Product { + description + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Different_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... ProductFragment1 @skip(if: $skip) + ... ProductFragment2 + } + } + + fragment ProductFragment1 on Product { + name + } + + fragment ProductFragment2 on Product { + averageRating + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Different_Subgraph_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + productBySlug(slug: $slug) { + ... ProductFragment1 @skip(if: true) + ... ProductFragment2 + } + } + + fragment ProductFragment1 on Product { + name + } + + fragment ProductFragment2 on Product { + averageRating + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... ProductFragment1 + ... ProductFragment2 @skip(if: $skip) + } + } + + fragment ProductFragment1 on Product { + name + } + + fragment ProductFragment2 on Product { + averageRating + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + productBySlug(slug: $slug) { + ... ProductFragment1 + ... ProductFragment2 @skip(if: true) + } + } + + fragment ProductFragment1 on Product { + name + } + + fragment ProductFragment2 on Product { + averageRating + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_From_Same_And_Different_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... ProductFragment @skip(if: $skip) + } + } + + fragment ProductFragment on Product { + name + averageRating + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_From_Same_And_Different_Subgraph_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + productBySlug(slug: $slug) { + ... ProductFragment @skip(if: true) + } + } + + fragment ProductFragment on Product { + name + averageRating + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_From_Different_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... ProductFragment @skip(if: $skip) + averageRating + } + } + + fragment ProductFragment on Product { + averageRating + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Different_Selection_From_Different_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $first: Int = 3, $skip: Boolean!) { + productBySlug(slug: $slug) { + averageRating + ... ProductFragment @skip(if: $skip) + } + } + + fragment ProductFragment on Product { + reviews(first: $first) { + nodes { + body + } + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assertx + plan.MatchSnapshot(); + } + + [Test] + public void Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Different_Selection_From_Different_Subgraph_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $first: Int = 3) { + productBySlug(slug: $slug) { + averageRating + ... ProductFragment @skip(if: true) + } + } + + fragment ProductFragment on Product { + reviews(first: $first) { + nodes { + body + } + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Doesn't work yet")] + public void Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_With_Same_Entry_Selection() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $first: Int = 3, $skip: Boolean!) { + productBySlug(slug: $slug) { + reviews(first: $first) { + pageInfo { + hasNextPage + } + } + ... ProductFragment @skip(if: $skip) + } + } + + fragment ProductFragment on Product { + reviews(first: $first) { + nodes { + body + } + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } + + [Test] + [Skip("Doesn't work yet")] + public void Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_With_Same_Entry_Selection_If_True() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!, $first: Int = 3) { + productBySlug(slug: $slug) { + reviews(first: $first) { + pageInfo { + hasNextPage + } + } + ... ProductFragment @skip(if: true) + } + } + + fragment ProductFragment on Product { + reviews(first: $first) { + nodes { + body + } + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } +} diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment.yaml new file mode 100644 index 00000000000..209922b49a8 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment.yaml @@ -0,0 +1,21 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + ... on Query @skip(if: $skip) { + productBySlug(slug: $slug) { + name + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + ... on Query { + productBySlug(slug: $slug) { + name + } + } + } + skipIf: "skip" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_If_True.yaml new file mode 100644 index 00000000000..3c5ef7a3cfc --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_If_True.yaml @@ -0,0 +1,10 @@ +request: + - document: >- + query($slug: String!) { + ... on Query @skip(if: true) { + productBySlug(slug: $slug) { + name + } + } + } +nodes: diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Same_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Same_Subgraph.yaml new file mode 100644 index 00000000000..098e683709c --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Same_Subgraph.yaml @@ -0,0 +1,30 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + ... on Query @skip(if: $skip) { + productBySlug(slug: $slug) { + name + } + } + products { + nodes { + description + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip: Boolean!, $slug: String!) { + ... on Query @skip(if: $skip) { + productBySlug(slug: $slug) { + name + } + } + products { + nodes { + description + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Same_Subgraph_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Same_Subgraph_If_True.yaml new file mode 100644 index 00000000000..0f9041344a2 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_Other_Not_Skipped_Root_Fragment_From_Same_Subgraph_If_True.yaml @@ -0,0 +1,25 @@ +request: + - document: >- + query($slug: String!) { + ... on Query @skip(if: true) { + productBySlug(slug: $slug) { + name + } + } + products { + nodes { + description + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + { + products { + nodes { + description + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_With_Selections_From_Two_Subgraphs_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_With_Selections_From_Two_Subgraphs_If_True.yaml new file mode 100644 index 00000000000..3c97c5b3bd5 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragment_With_Selections_From_Two_Subgraphs_If_True.yaml @@ -0,0 +1,13 @@ +request: + - document: >- + query($slug: String!) { + ... on Query @skip(if: true) { + productBySlug(slug: $slug) { + name + } + viewer { + displayName + } + } + } +nodes: diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragments_With_Same_Root_Selection_From_Same_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragments_With_Same_Root_Selection_From_Same_Subgraph.yaml new file mode 100644 index 00000000000..6fa819e751d --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragments_With_Same_Root_Selection_From_Same_Subgraph.yaml @@ -0,0 +1,32 @@ +request: + - document: >- + query($slug: String!, $skip1: Boolean!, $skip2: Boolean!) { + ... on Query @skip(if: $skip1) { + productBySlug(slug: $slug) { + name + } + } + ... on Query @skip(if: $skip2) { + productBySlug(slug: $slug) { + name + description + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip1: Boolean!, $skip2: Boolean!, $slug: String!) { + ... on Query @skip(if: $skip1) { + productBySlug(slug: $slug) { + name + } + } + ... on Query @skip(if: $skip2) { + productBySlug(slug: $slug) { + name + description + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragments_With_Same_Root_Selection_From_Same_Subgraph_Same_Variable.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragments_With_Same_Root_Selection_From_Same_Subgraph_Same_Variable.yaml new file mode 100644 index 00000000000..4e359ccc23d --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Fragments_With_Same_Root_Selection_From_Same_Subgraph_Same_Variable.yaml @@ -0,0 +1,33 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + ... on Query @skip(if: $skip) { + productBySlug(slug: $slug) { + name + } + } + ... on Query @skip(if: $skip) { + productBySlug(slug: $slug) { + name + description + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + ... on Query { + productBySlug(slug: $slug) { + name + } + } + ... on Query { + productBySlug(slug: $slug) { + name + description + } + } + } + skipIf: "skip" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Selection_Without_Skip_In_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Selection_Without_Skip_In_Fragment.yaml new file mode 100644 index 00000000000..9c5092b256c --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Selection_Without_Skip_In_Fragment.yaml @@ -0,0 +1,22 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) @skip(if: $skip) { + name + } + productBySlug(slug: $slug) { + name + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip: Boolean!, $slug: String!) { + productBySlug(slug: $slug) @skip(if: $skip) { + name + } + productBySlug(slug: $slug) { + name + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Skipped_Selection_In_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Skipped_Selection_In_Fragment.yaml new file mode 100644 index 00000000000..6b046c9e463 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Skipped_Selection_In_Fragment.yaml @@ -0,0 +1,17 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) @skip(if: $skip) { + name + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + name + } + } + skipIf: "skip" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Skipped_Selection_With_Different_Skip_In_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Skipped_Selection_With_Different_Skip_In_Fragment.yaml new file mode 100644 index 00000000000..10800a394ed --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Root_Selection_Same_Skipped_Selection_With_Different_Skip_In_Fragment.yaml @@ -0,0 +1,22 @@ +request: + - document: >- + query($slug: String!, $skip1: Boolean!, $skip2: Boolean!) { + productBySlug(slug: $slug) @skip(if: $skip1) { + name + } + productBySlug(slug: $slug) @skip(if: $skip2) { + name + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip1: Boolean!, $skip2: Boolean!, $slug: String!) { + productBySlug(slug: $slug) @skip(if: $skip1) { + name + } + productBySlug(slug: $slug) @skip(if: $skip2) { + name + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment.yaml new file mode 100644 index 00000000000..276ed9d8acf --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment.yaml @@ -0,0 +1,20 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + name + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip: Boolean!, $slug: String!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + name + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Different_Selection_From_Different_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Different_Selection_From_Different_Subgraph.yaml new file mode 100644 index 00000000000..b83b0cd1da0 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Different_Selection_From_Different_Subgraph.yaml @@ -0,0 +1,44 @@ +request: + - document: >- + query($slug: String!, $first: Int = 3, $skip: Boolean!) { + productBySlug(slug: $slug) { + averageRating + ... on Product @skip(if: $skip) { + reviews(first: $first) { + nodes { + body + } + } + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!, $first: Int = 3, $skip: Boolean!) { + productById(id: $__fusion_requirement_1) { + averageRating + ... on Product @skip(if: $skip) { + reviews(first: $first) { + nodes { + body + } + } + } + } + } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Different_Selection_From_Different_Subgraph_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Different_Selection_From_Different_Subgraph_If_True.yaml new file mode 100644 index 00000000000..3985e2ac8bf --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Different_Selection_From_Different_Subgraph_If_True.yaml @@ -0,0 +1,38 @@ +request: + - document: >- + query($slug: String!, $first: Int = 3) { + productBySlug(slug: $slug) { + averageRating + ... on Product @skip(if: true) { + reviews(first: $first) { + nodes { + body + } + } + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + __typename + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!) { + productById(id: $__fusion_requirement_1) { + averageRating + } + } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_From_Different_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_From_Different_Subgraph.yaml new file mode 100644 index 00000000000..c3e024faada --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_From_Different_Subgraph.yaml @@ -0,0 +1,36 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + averageRating + } + averageRating + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!, $skip: Boolean!) { + productById(id: $__fusion_requirement_1) { + ... on Product @skip(if: $skip) { + averageRating + } + averageRating + } + } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_With_Same_Entry_Selection.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_With_Same_Entry_Selection.yaml new file mode 100644 index 00000000000..46226129b9a --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_With_Same_Entry_Selection.yaml @@ -0,0 +1,52 @@ +request: + - document: >- + query($slug: String!, $first: Int = 3, $skip: Boolean!) { + productBySlug(slug: $slug) { + reviews(first: $first) { + pageInfo { + hasNextPage + } + } + ... on Product @skip(if: $skip) { + reviews(first: $first) { + nodes { + body + } + } + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!, $first: Int = 3, $skip: Boolean!) { + productById(id: $__fusion_requirement_1) { + reviews(first: $first) { + pageInfo { + hasNextPage + } + } + ... on Product @skip(if: $skip) { + reviews(first: $first) { + nodes { + body + } + } + } + } + } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_With_Same_Entry_Selection_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_With_Same_Entry_Selection_If_True.yaml new file mode 100644 index 00000000000..c8870d78971 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Selection_With_Same_Entry_Selection_If_True.yaml @@ -0,0 +1,46 @@ +request: + - document: >- + query($slug: String!, $first: Int = 3) { + productBySlug(slug: $slug) { + reviews(first: $first) { + pageInfo { + hasNextPage + } + } + ... on Product @skip(if: true) { + reviews(first: $first) { + nodes { + body + } + } + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + __typename + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!, $first: Int = 3) { + productById(id: $__fusion_requirement_1) { + reviews(first: $first) { + pageInfo { + hasNextPage + } + } + } + } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph.yaml new file mode 100644 index 00000000000..be06d954b7d --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph.yaml @@ -0,0 +1,37 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + name + ... on Product @skip(if: $skip) { + averageRating + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + name + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!) { + productById(id: $__fusion_requirement_1) { + ... on Product { + averageRating + } + } + } + skipIf: "skip" + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph_If_True.yaml new file mode 100644 index 00000000000..a7b43748048 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Different_Subgraph_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph_If_True.yaml @@ -0,0 +1,19 @@ +request: + - document: >- + query($slug: String!) { + productBySlug(slug: $slug) { + name + ... on Product @skip(if: true) { + averageRating + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + name + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Same_And_Different_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Same_And_Different_Subgraph.yaml new file mode 100644 index 00000000000..21ab6c7ff17 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Same_And_Different_Subgraph.yaml @@ -0,0 +1,39 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + name + averageRating + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip: Boolean!, $slug: String!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + name + } + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!) { + productById(id: $__fusion_requirement_1) { + ... on Product { + averageRating + } + } + } + skipIf: "skip" + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Same_And_Different_Subgraph_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Same_And_Different_Subgraph_If_True.yaml new file mode 100644 index 00000000000..7e5cc7dd2f0 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_From_Same_And_Different_Subgraph_If_True.yaml @@ -0,0 +1,19 @@ +request: + - document: >- + query($slug: String!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: true) { + name + averageRating + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + __typename + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_If_True.yaml new file mode 100644 index 00000000000..004834a8766 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_If_True.yaml @@ -0,0 +1,18 @@ +request: + - document: >- + query($slug: String!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: true) { + name + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + __typename + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Different_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Different_Subgraph.yaml new file mode 100644 index 00000000000..0f3ed2a9a03 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Different_Subgraph.yaml @@ -0,0 +1,36 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + name + } + averageRating + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip: Boolean!, $slug: String!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + name + } + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!) { + productById(id: $__fusion_requirement_1) { + averageRating + } + } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Different_Subgraph_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Different_Subgraph_If_True.yaml new file mode 100644 index 00000000000..320b43e4758 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Different_Subgraph_If_True.yaml @@ -0,0 +1,34 @@ +request: + - document: >- + query($slug: String!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: true) { + name + } + averageRating + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + __typename + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!) { + productById(id: $__fusion_requirement_1) { + averageRating + } + } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph.yaml new file mode 100644 index 00000000000..887fe2a2c4a --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph.yaml @@ -0,0 +1,22 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + name + } + description + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip: Boolean!, $slug: String!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: $skip) { + name + } + description + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph_If_True.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph_If_True.yaml new file mode 100644 index 00000000000..44e01035982 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Fragment_Other_Not_Skipped_Sub_Fragment_From_Same_Subgraph_If_True.yaml @@ -0,0 +1,19 @@ +request: + - document: >- + query($slug: String!) { + productBySlug(slug: $slug) { + ... on Product @skip(if: true) { + name + } + description + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + description + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Selection_Without_Skip_In_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Selection_Without_Skip_In_Fragment.yaml new file mode 100644 index 00000000000..b61aaaf0c18 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Selection_Without_Skip_In_Fragment.yaml @@ -0,0 +1,22 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + name + ... on Product @skip(if: $skip) { + name + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip: Boolean!, $slug: String!) { + productBySlug(slug: $slug) { + name + ... on Product @skip(if: $skip) { + name + } + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Skipped_Selection_In_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Skipped_Selection_In_Fragment.yaml new file mode 100644 index 00000000000..a043447b011 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Skipped_Selection_In_Fragment.yaml @@ -0,0 +1,16 @@ +request: + - document: >- + query($slug: String!, $skip: Boolean!) { + productBySlug(slug: $slug) { + name @skip(if: $skip) + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip: Boolean!, $slug: String!) { + productBySlug(slug: $slug) { + name @skip(if: $skip) + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Skipped_Selection_With_Different_Skip_In_Fragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Skipped_Selection_With_Different_Skip_In_Fragment.yaml new file mode 100644 index 00000000000..0a139c42871 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/ConditionTests/__snapshots__/SkipFragmentTests.Skipped_Sub_Selection_Same_Skipped_Selection_With_Different_Skip_In_Fragment.yaml @@ -0,0 +1,18 @@ +request: + - document: >- + query($slug: String!, $skip1: Boolean!, $skip2: Boolean!) { + productBySlug(slug: $slug) { + name @skip(if: $skip1) + name @skip(if: $skip2) + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($skip1: Boolean!, $skip2: Boolean!, $slug: String!) { + productBySlug(slug: $slug) { + name @skip(if: $skip1) + name @skip(if: $skip2) + } + } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/FragmentTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/FragmentTests.cs index 03c1a8de6a9..b5aa4fb5f56 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/FragmentTests.cs +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/FragmentTests.cs @@ -450,4 +450,43 @@ fragment ProductFragment2 on Product { // assert plan.MatchSnapshot(); } + + [Test] + public void Two_Fragments_On_Sub_Selection_With_Different_But_Same_Entry_Selection_From_Different_Subgraph() + { + // arrange + var compositeSchema = CreateCompositeSchema(); + + var request = Parse( + """ + query($slug: String!) { + productBySlug(slug: $slug) { + ...ProductFragment1 + ...ProductFragment2 + } + } + + fragment ProductFragment1 on Product { + reviews { + nodes { + body + } + } + } + + fragment ProductFragment2 on Product { + reviews { + pageInfo { + hasNextPage + } + } + } + """); + + // act + var plan = PlanOperation(request, compositeSchema); + + // assert + plan.MatchSnapshot(); + } } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/__snapshots__/FragmentTests.Two_Fragments_On_Sub_Selection_With_Different_But_Same_Entry_Selection_From_Different_Subgraph.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/__snapshots__/FragmentTests.Two_Fragments_On_Sub_Selection_With_Different_But_Same_Entry_Selection_From_Different_Subgraph.yaml new file mode 100644 index 00000000000..f6c6193faa4 --- /dev/null +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/__snapshots__/FragmentTests.Two_Fragments_On_Sub_Selection_With_Different_But_Same_Entry_Selection_From_Different_Subgraph.yaml @@ -0,0 +1,48 @@ +request: + - document: >- + query($slug: String!) { + productBySlug(slug: $slug) { + reviews { + nodes { + body + } + } + reviews { + pageInfo { + hasNextPage + } + } + } + } +nodes: + - id: 1 + schema: "PRODUCTS" + operation: >- + query($slug: String!) { + productBySlug(slug: $slug) { + id + } + } + - id: 2 + schema: "REVIEWS" + operation: >- + query($__fusion_requirement_1: ID!) { + productById(id: $__fusion_requirement_1) { + reviews { + nodes { + body + } + } + reviews { + pageInfo { + hasNextPage + } + } + } + } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productBySlug" + field: "id" + type: "ID!" diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/__snapshots__/OperationPlannerTests.Plan_With_Conditional_InlineFragment.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/__snapshots__/OperationPlannerTests.Plan_With_Conditional_InlineFragment.yaml index fb17a424b87..368cd5c2b46 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/__snapshots__/OperationPlannerTests.Plan_With_Conditional_InlineFragment.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/__snapshots__/OperationPlannerTests.Plan_With_Conditional_InlineFragment.yaml @@ -17,5 +17,22 @@ nodes: productById(id: 1) { id name + id + } + } + - id: 2 + schema: "SHIPPING" + operation: >- + query($__fusion_requirement_1: ID!) { + productById(id: $__fusion_requirement_1) { + ... on Product { + estimatedDelivery(postCode: "12345") + } } } + requirements: + - name: "__fusion_requirement_1" + dependsOn: "1" + selectionSet: "productById" + field: "id" + type: "ID!"