Skip to content

Commit

Permalink
Extract error path from context.
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib committed Mar 4, 2024
1 parent 7450069 commit 7f24b6b
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 17 deletions.
22 changes: 20 additions & 2 deletions src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Buffers;
using System.Text.Json;

namespace HotChocolate.Execution.Processing;

internal static class PathHelper
{
private const int _initialPathLength = 64;
private const int _initialPathLength = 64;

public static Path CreatePathFromContext(ObjectResult parent)
=> CreatePath(parent);

Expand All @@ -18,6 +19,23 @@ public static Path CreatePathFromContext(ISelection selection, ResultData parent
_ => throw new NotSupportedException($"{parent.GetType().FullName} is not a supported parent type."),
};

public static Path CombinePath(ObjectResult parent, JsonElement errorSubPath, int skipSubElements)
{
var path = parent.Parent is null ? Path.Root : CreatePath(parent);

for (var i = skipSubElements; i < errorSubPath.GetArrayLength(); i++)
{
path = errorSubPath[i] switch
{
{ ValueKind: JsonValueKind.String, } nameElement => path.Append(nameElement.GetString()!),
{ ValueKind: JsonValueKind.Number, } indexElement => path.Append(indexElement.GetInt32()),
_ => throw new InvalidOperationException("The error path contains an unsupported element.")
};
}

return path;
}

private static Path CreatePath(ResultData parent, object segmentValue)
{
var segments = ArrayPool<object>.Shared.Rent(_initialPathLength);
Expand Down
4 changes: 4 additions & 0 deletions src/HotChocolate/Fusion/src/Core/Execution/ExecutionState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@ public ExecutionState(

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

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

Expand Down
10 changes: 8 additions & 2 deletions src/HotChocolate/Fusion/src/Core/Execution/ExecutorUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ public static void TryInitializeExecutionState(QueryPlan queryPlan, ExecutionSta
public static void ExtractErrors(
ResultBuilder resultBuilder,
JsonElement errors,
ObjectResult selectionSetResult,
int pathDepth,
bool addDebugInfo)
{
if (errors.ValueKind is not JsonValueKind.Array)
Expand All @@ -492,13 +494,15 @@ public static void ExtractErrors(

foreach (var error in errors.EnumerateArray())
{
ExtractError(resultBuilder, error, addDebugInfo);
ExtractError(resultBuilder, error, selectionSetResult, pathDepth, addDebugInfo);
}
}

private static void ExtractError(
ResultBuilder resultBuilder,
JsonElement error,
ObjectResult selectionSetResult,
int pathDepth,
bool addDebugInfo)
{
if (error.ValueKind is not JsonValueKind.Object)
Expand Down Expand Up @@ -530,7 +534,9 @@ private static void ExtractError(
if (error.TryGetProperty("path", out var remotePath) &&
remotePath.ValueKind is JsonValueKind.Array)
{
// TODO : rewrite remote path if possible!
var path = PathHelper.CombinePath(selectionSetResult, remotePath, pathDepth);
errorBuilder.SetPath(path);

if (addDebugInfo)
{
errorBuilder.SetExtension("remotePath", remotePath);
Expand Down
13 changes: 7 additions & 6 deletions src/HotChocolate/Fusion/src/Core/Execution/Nodes/Resolve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace HotChocolate.Fusion.Execution.Nodes;
/// </param>
internal sealed class Resolve(int id, Config config) : ResolverNodeBase(id, config)
{

/// <summary>
/// Gets the kind of this node.
/// </summary>
Expand Down Expand Up @@ -69,7 +68,7 @@ protected override async Task OnExecuteAsync(
ProcessResponses(context, executionState, requests, responses, SubgraphName);
}
}
catch(Exception ex)
catch (Exception ex)
{
var error = context.OperationContext.ErrorHandler.CreateUnexpectedError(ex);
context.Result.AddError(error.Build());
Expand Down Expand Up @@ -129,20 +128,22 @@ private void ProcessResponses(
ref var request = ref MemoryMarshal.GetArrayDataReference(requests);
ref var response = ref MemoryMarshal.GetArrayDataReference(responses);
ref var end = ref Unsafe.Add(ref state, executionStates.Count);
var pathLength = Path.Length;

while (Unsafe.IsAddressLessThan(ref state, ref end))
{
var data = UnwrapResult(response);
var selectionSet = state.SelectionSet;
var selectionResults = state.SelectionSetData;
var selectionSetData = state.SelectionSetData;
var selectionSetResult = state.SelectionSetResult;
var exportKeys = state.Requires;
var variableValues = state.VariableValues;

ExtractErrors(context.Result, response.Errors, context.ShowDebugInfo);
ExtractErrors(context.Result, response.Errors, selectionSetResult, pathLength, context.ShowDebugInfo);

// we extract the selection data from the request and add it to the
// workItem results.
ExtractSelectionResults(SelectionSet, subgraphName, data, selectionResults);
ExtractSelectionResults(SelectionSet, subgraphName, data, selectionSetData);

// next we need to extract any variables that we need for followup requests.
ExtractVariables(data, context.QueryPlan, selectionSet, exportKeys, variableValues);
Expand All @@ -152,4 +153,4 @@ private void ProcessResponses(
response = ref Unsafe.Add(ref response, 1)!;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using HotChocolate.Execution.Processing;
using HotChocolate.Fusion.Clients;
using HotChocolate.Language;
using static HotChocolate.Fusion.Execution.ExecutorUtils;
Expand Down Expand Up @@ -87,7 +88,7 @@ protected override async Task OnExecuteAsync(
ProcessResult(context, response, batchExecutionState);
}
}
catch(Exception ex)
catch (Exception ex)
{
var error = context.OperationContext.ErrorHandler.CreateUnexpectedError(ex);
context.Result.AddError(error.Build());
Expand Down Expand Up @@ -134,17 +135,28 @@ private void ProcessResult(
GraphQLResponse response,
BatchExecutionState[] batchExecutionState)
{
ExtractErrors(context.Result, response.Errors, context.ShowDebugInfo);
var result = UnwrapResult(response, Requires);

ref var batchState = ref MemoryMarshal.GetArrayDataReference(batchExecutionState);
ref var end = ref Unsafe.Add(ref batchState, batchExecutionState.Length);
var pathLength = Path.Length;
var first = true;

while (Unsafe.IsAddressLessThan(ref batchState, ref end))
{
if (result.TryGetValue(batchState.Key, out var data))
{
ExtractSelectionResults(SelectionSet, SubgraphName, data, batchState.SelectionResults);
if (first)
{
ExtractErrors(
context.Result,
response.Errors,
batchState.SelectionSetResult,
pathLength,
context.ShowDebugInfo);
first = false;
}

ExtractSelectionResults(SelectionSet, SubgraphName, data, batchState.SelectionSetData);
ExtractVariables(data, context.QueryPlan, SelectionSet, batchState.Requires, batchState.VariableValues);
}

Expand Down Expand Up @@ -237,6 +249,7 @@ private Dictionary<string, JsonElement> UnwrapResult(
if (exportKeys.Count == 1)
{
var key = exportKeys[0];

foreach (var element in data.EnumerateArray())
{
if (element.TryGetProperty(key, out var keyValue))
Expand Down Expand Up @@ -391,8 +404,17 @@ private readonly struct BatchExecutionState(string batchKey, ExecutionState exec
public IReadOnlyList<string> Requires { get; } = executionState.Requires;

/// <summary>
/// Gets the selection set data.
/// Gets the completed selection set result.
/// The selection set result represents the data for the
/// <see cref="ExecutionState.SelectionSet"/> that we deliver to the user.
/// </summary>
public ObjectResult SelectionSetResult { get; } = executionState.SelectionSetResult;

/// <summary>
/// Gets the selection set data that was collected during execution.
/// The selection set data represents the data that we have collected
/// from the subgraphs for the <see cref="ExecutionState.SelectionSet"/>.
/// </summary>
public SelectionData[] SelectionResults { get; } = executionState.SelectionSetData;
public SelectionData[] SelectionSetData { get; } = executionState.SelectionSetData;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ Result
"column": 101
}
],
"path": [
"reviewById",
"author",
"errorField"
],
"extensions": {
"remotePath": [
"userById",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ Result
"column": 84
}
],
"path": [
"userById",
"reviews",
1,
"errorField"
],
"extensions": {
"remotePath": [
"userById",
Expand All @@ -96,6 +102,12 @@ Result
"column": 84
}
],
"path": [
"userById",
"reviews",
0,
"errorField"
],
"extensions": {
"remotePath": [
"userById",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ Result
"column": 94
}
],
"path": [
"reviews",
0,
"author",
"errorField"
],
"extensions": {
"remotePath": [
"usersById",1,
Expand All @@ -95,6 +101,12 @@ Result
"column": 94
}
],
"path": [
"reviews",
0,
"author",
"errorField"
],
"extensions": {
"remotePath": [
"usersById",0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ Result
"column": 68
}
],
"path": [
"errorField"
],
"extensions": {
"remotePath": [
"errorField"
Expand Down

0 comments on commit 7f24b6b

Please sign in to comment.