Skip to content

Commit 9479306

Browse files
Include path for Fusion subgraph errors (#6916)
Co-authored-by: Michael Staib <[email protected]>
1 parent ed0449f commit 9479306

16 files changed

+1352
-27
lines changed

src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
using System;
22
using System.Buffers;
3+
using System.Text.Json;
34

45
namespace HotChocolate.Execution.Processing;
56

67
internal static class PathHelper
78
{
8-
private const int _initialPathLength = 64;
9-
9+
private const int _initialPathLength = 64;
10+
1011
public static Path CreatePathFromContext(ObjectResult parent)
11-
=> CreatePath(parent);
12+
=> parent.Parent is null ? Path.Root : CreatePath(parent);
1213

1314
public static Path CreatePathFromContext(ISelection selection, ResultData parent, int index)
1415
=> parent switch
@@ -18,6 +19,21 @@ public static Path CreatePathFromContext(ISelection selection, ResultData parent
1819
_ => throw new NotSupportedException($"{parent.GetType().FullName} is not a supported parent type."),
1920
};
2021

22+
public static Path CombinePath(Path path, JsonElement errorSubPath, int skipSubElements)
23+
{
24+
for (var i = skipSubElements; i < errorSubPath.GetArrayLength(); i++)
25+
{
26+
path = errorSubPath[i] switch
27+
{
28+
{ ValueKind: JsonValueKind.String, } nameElement => path.Append(nameElement.GetString()!),
29+
{ ValueKind: JsonValueKind.Number, } indexElement => path.Append(indexElement.GetInt32()),
30+
_ => throw new InvalidOperationException("The error path contains an unsupported element.")
31+
};
32+
}
33+
34+
return path;
35+
}
36+
2137
private static Path CreatePath(ResultData parent, object segmentValue)
2238
{
2339
var segments = ArrayPool<object>.Shared.Rent(_initialPathLength);

src/HotChocolate/Fusion/src/Core/Execution/ExecutionState.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ public ExecutionState(
4141

4242
/// <summary>
4343
/// Gets the selection set data that was collected during execution.
44+
/// The selection set data represents the data that we have collected
45+
/// from the subgraphs for the <see cref="SelectionSet"/>.
4446
/// </summary>
4547
public SelectionData[] SelectionSetData { get; }
4648

4749
/// <summary>
4850
/// Gets the completed selection set result.
51+
/// The selection set result represents the data for the
52+
/// <see cref="SelectionSet"/> that we deliver to the user.
4953
/// </summary>
5054
public ObjectResult SelectionSetResult { get; }
5155

src/HotChocolate/Fusion/src/Core/Execution/ExecutorUtils.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,22 +483,28 @@ public static void TryInitializeExecutionState(QueryPlan queryPlan, ExecutionSta
483483
public static void ExtractErrors(
484484
ResultBuilder resultBuilder,
485485
JsonElement errors,
486+
ObjectResult selectionSetResult,
487+
int pathDepth,
486488
bool addDebugInfo)
487489
{
488490
if (errors.ValueKind is not JsonValueKind.Array)
489491
{
490492
return;
491493
}
492494

495+
var path = PathHelper.CreatePathFromContext(selectionSetResult);
493496
foreach (var error in errors.EnumerateArray())
494497
{
495-
ExtractError(resultBuilder, error, addDebugInfo);
498+
ExtractError(resultBuilder, error, selectionSetResult, path, pathDepth, addDebugInfo);
496499
}
497500
}
498501

499502
private static void ExtractError(
500503
ResultBuilder resultBuilder,
501504
JsonElement error,
505+
ObjectResult selectionSetResult,
506+
Path parentPath,
507+
int pathDepth,
502508
bool addDebugInfo)
503509
{
504510
if (error.ValueKind is not JsonValueKind.Object)
@@ -530,7 +536,9 @@ private static void ExtractError(
530536
if (error.TryGetProperty("path", out var remotePath) &&
531537
remotePath.ValueKind is JsonValueKind.Array)
532538
{
533-
// TODO : rewrite remote path if possible!
539+
var path = PathHelper.CombinePath(parentPath, remotePath, pathDepth);
540+
errorBuilder.SetPath(path);
541+
534542
if (addDebugInfo)
535543
{
536544
errorBuilder.SetExtension("remotePath", remotePath);

src/HotChocolate/Fusion/src/Core/Execution/Nodes/Resolve.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ namespace HotChocolate.Fusion.Execution.Nodes;
1717
/// </param>
1818
internal sealed class Resolve(int id, Config config) : ResolverNodeBase(id, config)
1919
{
20-
2120
/// <summary>
2221
/// Gets the kind of this node.
2322
/// </summary>
@@ -69,7 +68,7 @@ protected override async Task OnExecuteAsync(
6968
ProcessResponses(context, executionState, requests, responses, SubgraphName);
7069
}
7170
}
72-
catch(Exception ex)
71+
catch (Exception ex)
7372
{
7473
var error = context.OperationContext.ErrorHandler.CreateUnexpectedError(ex);
7574
context.Result.AddError(error.Build());
@@ -129,20 +128,22 @@ private void ProcessResponses(
129128
ref var request = ref MemoryMarshal.GetArrayDataReference(requests);
130129
ref var response = ref MemoryMarshal.GetArrayDataReference(responses);
131130
ref var end = ref Unsafe.Add(ref state, executionStates.Count);
131+
var pathLength = Path.Length;
132132

133133
while (Unsafe.IsAddressLessThan(ref state, ref end))
134134
{
135135
var data = UnwrapResult(response);
136136
var selectionSet = state.SelectionSet;
137-
var selectionResults = state.SelectionSetData;
137+
var selectionSetData = state.SelectionSetData;
138+
var selectionSetResult = state.SelectionSetResult;
138139
var exportKeys = state.Requires;
139140
var variableValues = state.VariableValues;
140141

141-
ExtractErrors(context.Result, response.Errors, context.ShowDebugInfo);
142+
ExtractErrors(context.Result, response.Errors, selectionSetResult, pathLength, context.ShowDebugInfo);
142143

143144
// we extract the selection data from the request and add it to the
144145
// workItem results.
145-
ExtractSelectionResults(SelectionSet, subgraphName, data, selectionResults);
146+
ExtractSelectionResults(SelectionSet, subgraphName, data, selectionSetData);
146147

147148
// next we need to extract any variables that we need for followup requests.
148149
ExtractVariables(data, context.QueryPlan, selectionSet, exportKeys, variableValues);
@@ -152,4 +153,4 @@ private void ProcessResponses(
152153
response = ref Unsafe.Add(ref response, 1)!;
153154
}
154155
}
155-
}
156+
}

src/HotChocolate/Fusion/src/Core/Execution/Nodes/ResolveByKeyBatch.cs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Runtime.InteropServices;
33
using System.Text;
44
using System.Text.Json;
5+
using HotChocolate.Execution.Processing;
56
using HotChocolate.Fusion.Clients;
67
using HotChocolate.Language;
78
using static HotChocolate.Fusion.Execution.ExecutorUtils;
@@ -87,7 +88,7 @@ protected override async Task OnExecuteAsync(
8788
ProcessResult(context, response, batchExecutionState);
8889
}
8990
}
90-
catch(Exception ex)
91+
catch (Exception ex)
9192
{
9293
var error = context.OperationContext.ErrorHandler.CreateUnexpectedError(ex);
9394
context.Result.AddError(error.Build());
@@ -134,17 +135,28 @@ private void ProcessResult(
134135
GraphQLResponse response,
135136
BatchExecutionState[] batchExecutionState)
136137
{
137-
ExtractErrors(context.Result, response.Errors, context.ShowDebugInfo);
138138
var result = UnwrapResult(response, Requires);
139-
140139
ref var batchState = ref MemoryMarshal.GetArrayDataReference(batchExecutionState);
141140
ref var end = ref Unsafe.Add(ref batchState, batchExecutionState.Length);
141+
var pathLength = Path.Length;
142+
var first = true;
142143

143144
while (Unsafe.IsAddressLessThan(ref batchState, ref end))
144145
{
146+
if (first)
147+
{
148+
ExtractErrors(
149+
context.Result,
150+
response.Errors,
151+
batchState.SelectionSetResult,
152+
pathLength + 1,
153+
context.ShowDebugInfo);
154+
first = false;
155+
}
156+
145157
if (result.TryGetValue(batchState.Key, out var data))
146158
{
147-
ExtractSelectionResults(SelectionSet, SubgraphName, data, batchState.SelectionResults);
159+
ExtractSelectionResults(SelectionSet, SubgraphName, data, batchState.SelectionSetData);
148160
ExtractVariables(data, context.QueryPlan, SelectionSet, batchState.Requires, batchState.VariableValues);
149161
}
150162

@@ -237,6 +249,7 @@ private Dictionary<string, JsonElement> UnwrapResult(
237249
if (exportKeys.Count == 1)
238250
{
239251
var key = exportKeys[0];
252+
240253
foreach (var element in data.EnumerateArray())
241254
{
242255
if (element.TryGetProperty(key, out var keyValue))
@@ -391,8 +404,17 @@ private readonly struct BatchExecutionState(string batchKey, ExecutionState exec
391404
public IReadOnlyList<string> Requires { get; } = executionState.Requires;
392405

393406
/// <summary>
394-
/// Gets the selection set data.
407+
/// Gets the completed selection set result.
408+
/// The selection set result represents the data for the
409+
/// <see cref="ExecutionState.SelectionSet"/> that we deliver to the user.
410+
/// </summary>
411+
public ObjectResult SelectionSetResult { get; } = executionState.SelectionSetResult;
412+
413+
/// <summary>
414+
/// Gets the selection set data that was collected during execution.
415+
/// The selection set data represents the data that we have collected
416+
/// from the subgraphs for the <see cref="ExecutionState.SelectionSet"/>.
395417
/// </summary>
396-
public SelectionData[] SelectionResults { get; } = executionState.SelectionSetData;
418+
public SelectionData[] SelectionSetData { get; } = executionState.SelectionSetData;
397419
}
398-
}
420+
}

0 commit comments

Comments
 (0)