From b83765341bc05afdc02ab6eb72377b1dc1049331 Mon Sep 17 00:00:00 2001 From: tobias-tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:00:53 +0100 Subject: [PATCH 1/2] Add reproduction --- .../test/Core.Tests/DemoIntegrationTests.cs | 64 +++++++++++++++++++ ...tion_With_Selection_On_Another_Subgraph.md | 28 ++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph.md diff --git a/src/HotChocolate/Fusion/test/Core.Tests/DemoIntegrationTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/DemoIntegrationTests.cs index 1c27d956d6f..2c92c84cd54 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/DemoIntegrationTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/DemoIntegrationTests.cs @@ -2178,6 +2178,70 @@ type ResaleSurveyFeedback { await snapshot.MatchMarkdownAsync(); } + [Fact] + public async Task Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + viewer: Viewer + } + + type Mutation { + doSomething: DoSomethingPayload + } + + type DoSomethingPayload { + something: Int + viewer: Viewer + } + + type Viewer { + subgraphA: String + } + """); + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + viewer: Viewer + } + + type Viewer { + subgraphB: String + } + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + var executor = await subgraphs.GetExecutorAsync(); + + var request = Parse( + """ + mutation { + doSomething { + something + viewer { + subgraphA + subgraphB + } + } + } + + """); + + // act + await using var result = await executor.ExecuteAsync( + OperationRequestBuilder + .New() + .SetDocument(request) + .Build()); + + // assert + var snapshot = new Snapshot(); + CollectSnapshotData(snapshot, request, result); + await snapshot.MatchMarkdownAsync(); + } + public sealed class HotReloadConfiguration : IObservable { private GatewayConfiguration _configuration; diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph.md new file mode 100644 index 00000000000..e3af26f7c69 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph.md @@ -0,0 +1,28 @@ +# Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph + +## Result + +```json +{ + "errors": [ + { + "message": "Unexpected Execution Error" + } + ] +} +``` + +## Request + +```graphql +mutation { + doSomething { + something + viewer { + subgraphA + subgraphB + } + } +} +``` + From e23e2518fdbd47675e5e119464015d85aab08a62 Mon Sep 17 00:00:00 2001 From: tobias-tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:49:56 +0100 Subject: [PATCH 2/2] Add @resolver to Viewer type --- .../src/Composition/FusionGraphComposer.cs | 1 + .../Composition/Pipeline/NodeMiddleware.cs | 4 +- .../Composition/Pipeline/ViewerMiddleware.cs | 35 +++++++++++ ...tion_With_Selection_On_Another_Subgraph.md | 58 +++++++++++++++++-- 4 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 src/HotChocolate/Fusion/src/Composition/Pipeline/ViewerMiddleware.cs diff --git a/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs b/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs index 4d87c747ab5..34855489646 100644 --- a/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs +++ b/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs @@ -73,6 +73,7 @@ internal FusionGraphComposer( .Use() .Use() .Use() + .Use() .Use() .Use() .Use() diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/NodeMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/NodeMiddleware.cs index 8a768b32f9f..f6e9b9ac710 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/NodeMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/NodeMiddleware.cs @@ -13,10 +13,8 @@ public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate nex if (fusionGraph.QueryType is not null && context.Features.IsNodeFieldSupported() && - fusionGraph.QueryType.Fields.TryGetField("node", out var nodeField)) + fusionGraph.QueryType.Fields.ContainsName("node")) { - fusionGraph.QueryType.Fields.TryGetField("nodes", out var nodesField); - var nodes = new HashSet(); foreach (var schema in context.Subgraphs) diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/ViewerMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/ViewerMiddleware.cs new file mode 100644 index 00000000000..b49013e1f56 --- /dev/null +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/ViewerMiddleware.cs @@ -0,0 +1,35 @@ +using HotChocolate.Language; +using HotChocolate.Skimmed; + +namespace HotChocolate.Fusion.Composition.Pipeline; + +internal sealed class ViewerMiddleware : IMergeMiddleware +{ + public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate next) + { + var fusionGraph = context.FusionGraph; + var fusionTypes = context.FusionTypes; + + if (fusionGraph.QueryType is not null && + fusionGraph.QueryType.Fields.TryGetField("viewer", out var viewerField) && + viewerField.Type.NamedType() is ObjectTypeDefinition { Name: "Viewer" } viewerType) + { + var viewerSelectionNode = new SelectionSetNode([new FieldNode("viewer")]); + + foreach (var subgraphSchema in context.Subgraphs) + { + if (subgraphSchema.QueryType is not null && + subgraphSchema.QueryType.Fields.TryGetField("viewer", out var subgraphViewerField) && + subgraphViewerField.Type.NamedType() is ObjectTypeDefinition { Name: "Viewer" }) + { + viewerType.Directives.Add(fusionTypes.CreateResolverDirective(subgraphSchema.Name, viewerSelectionNode)); + } + } + } + + if (!context.Log.HasErrors) + { + await next(context).ConfigureAwait(false); + } + } +} diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph.md index e3af26f7c69..be2ad1778ce 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Viewer_Returned_From_Mutation_With_Selection_On_Another_Subgraph.md @@ -4,11 +4,15 @@ ```json { - "errors": [ - { - "message": "Unexpected Execution Error" + "data": { + "doSomething": { + "something": 123, + "viewer": { + "subgraphA": "string", + "subgraphB": "string" + } } - ] + } } ``` @@ -26,3 +30,49 @@ mutation { } ``` +## QueryPlan Hash + +```text +3BAD760CBB742706BF2DEDB9CA40F8DA880B22CC +``` + +## QueryPlan + +```json +{ + "document": "mutation { doSomething { something viewer { subgraphA subgraphB } } }", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "mutation fetch_doSomething_1 { doSomething { something viewer { subgraphA } } }", + "selectionSetId": 0 + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + }, + { + "type": "Resolve", + "subgraph": "Subgraph_2", + "document": "query fetch_doSomething_2 { viewer { subgraphB } }", + "selectionSetId": 2, + "path": [ + "viewer" + ] + }, + { + "type": "Compose", + "selectionSetIds": [ + 2 + ] + } + ] + } +} +``` +