From 6df17269a3e471f6aed112a53484f15d17bced33 Mon Sep 17 00:00:00 2001 From: Enterprize1 Date: Mon, 19 May 2025 13:32:47 +0200 Subject: [PATCH 1/2] [Fusion] Add support for result from different subgraph on interfaces --- .../ExecutionStepDiscoveryMiddleware.cs | 38 +++++++++-- .../test/Core.Tests/RequestPlannerTests.cs | 65 ++++++++++++++++++ ...Requested_On_Interface_Called_Only_Once.md | 67 +++++++++++++++++++ 3 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Subgraph_Requested_On_Interface_Called_Only_Once.md diff --git a/src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs b/src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs index 3b249639ccd..39df41e09e6 100644 --- a/src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs @@ -435,16 +435,40 @@ private void CollectNestedSelections( subgraph.Add(executionStep.SubgraphName); } - backlog.Enqueue( - new BacklogItem( - parentSelection, - selectionSetPath, - declaringType, - leftovers, - preferBatching)); + TryEnqueueBacklogItem( + backlog, + parentSelection, + selectionSetPath, + declaringType, + leftovers, + preferBatching + ); } } + private static void TryEnqueueBacklogItem(Queue backlog, ISelection parentSelection, SelectionPath? selectionSetPath, ObjectTypeMetadata declaringType, List leftovers, bool preferBatching) + { + foreach (var item in backlog) + { + if ((item.SelectionPath?.Equals(selectionSetPath) ?? selectionSetPath is null) && + item.DeclaringTypeMetadata == declaringType && + item.PreferBatching == preferBatching && + item.Selections.Count == leftovers.Count && + item.Selections.SequenceEqual(leftovers)) + { + return; + } + } + + backlog.Enqueue( + new BacklogItem( + parentSelection, + selectionSetPath, + declaringType, + leftovers, + preferBatching)); + } + private static SelectionPath? CreateSelectionPath(SelectionPath? rootPath, List pathSegments) { var parent = rootPath; diff --git a/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs index 2cad49c2bcd..c23e51b1624 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs @@ -2794,6 +2794,71 @@ type Query { await snapshot.MatchMarkdownAsync(); } + [Fact] + public async Task Subgraph_Requested_On_Interface_Called_Only_Once() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + schema { + query: Query + } + + type Query { + books: [Book!]! + } + + interface Book { + author: Author! + } + + type FunnyBook implements Book { + author: Author! + } + + type ScaryBook implements Book { + author: Author! + } + + type Author { + id: Int! + } + """ + ); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + schema { + query: Query + } + + type Query { + authorById(id: [Int!]!): [Author!]! + } + + type Author { + id: Int! + name: String! + } + """ + ); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + //"query { subgraph2Foo { name } }"); + "query { books { author { name } } }"); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + private static async Task<(DocumentNode UserRequest, Execution.Nodes.QueryPlan QueryPlan)> CreateQueryPlanAsync( Skimmed.SchemaDefinition fusionGraph, [StringSyntax("graphql")] string query) diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Subgraph_Requested_On_Interface_Called_Only_Once.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Subgraph_Requested_On_Interface_Called_Only_Once.md new file mode 100644 index 00000000000..b33cede1533 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Subgraph_Requested_On_Interface_Called_Only_Once.md @@ -0,0 +1,67 @@ +# Subgraph_Requested_On_Interface_Called_Only_Once + +## UserRequest + +```graphql +{ + books { + author { + name + } + } +} +``` + +## QueryPlan + +```json +{ + "document": "{ books { author { name } } }", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "query fetch_books_1 { books { __typename ... on ScaryBook { author { __fusion_exports__1: id } } ... on FunnyBook { author { __fusion_exports__1: id } } } }", + "selectionSetId": 0, + "provides": [ + { + "variable": "__fusion_exports__1" + } + ] + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + }, + { + "type": "ResolveByKeyBatch", + "subgraph": "Subgraph_2", + "document": "query fetch_books_2($__fusion_exports__1: [Int!]!) { authorById(id: $__fusion_exports__1) { name __fusion_exports__1: id } }", + "selectionSetId": 3, + "path": [ + "authorById" + ], + "requires": [ + { + "variable": "__fusion_exports__1" + } + ] + }, + { + "type": "Compose", + "selectionSetIds": [ + 3 + ] + } + ] + }, + "state": { + "__fusion_exports__1": "Author_id" + } +} +``` + From c0c1c0fcc018afe38551ca187753903eaa7b817c Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 23 May 2025 13:43:24 +0200 Subject: [PATCH 2/2] Update src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs --- .../Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs b/src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs index 39df41e09e6..90d955cd349 100644 --- a/src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Core/Planning/Pipeline/ExecutionStepDiscoveryMiddleware.cs @@ -446,7 +446,13 @@ private void CollectNestedSelections( } } - private static void TryEnqueueBacklogItem(Queue backlog, ISelection parentSelection, SelectionPath? selectionSetPath, ObjectTypeMetadata declaringType, List leftovers, bool preferBatching) + private static void TryEnqueueBacklogItem( + Queue backlog, + ISelection parentSelection, + SelectionPath? selectionSetPath, + ObjectTypeMetadata declaringType, + List leftovers, + bool preferBatching) { foreach (var item in backlog) {