Skip to content

Commit 11e16e1

Browse files
Connect parent to child to fix paths
1 parent 9e0fa33 commit 11e16e1

15 files changed

+608
-104
lines changed

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Clients/ErrorTrie.cs

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,12 @@
22

33
namespace HotChocolate.Fusion.Execution.Clients;
44

5-
// TODO: Add docs
65
public sealed class ErrorTrie : Dictionary<object, ErrorTrie>
76
{
8-
public JsonElement Error { get; private set; }
7+
public JsonElement Error { get; set; }
98

10-
public static ErrorTrie? From(JsonElement json)
9+
public JsonElement? GetFirstError()
1110
{
12-
if (json.ValueKind != JsonValueKind.Array)
13-
{
14-
return null;
15-
}
16-
17-
var root = new ErrorTrie();
18-
19-
foreach (var error in json.EnumerateArray())
20-
{
21-
var currentTrie = root;
22-
23-
if (!error.TryGetProperty("path", out var path) || path.ValueKind != JsonValueKind.Array)
24-
{
25-
continue;
26-
}
27-
28-
foreach (var pathSegment in path.EnumerateArray())
29-
{
30-
object? pathSegmentValue = pathSegment.ValueKind switch
31-
{
32-
JsonValueKind.String => pathSegment.GetString(),
33-
JsonValueKind.Number => pathSegment.GetInt32(),
34-
_ => null
35-
};
36-
37-
if (pathSegmentValue is null)
38-
{
39-
break;
40-
}
41-
42-
if (currentTrie.TryGetValue(pathSegmentValue, out var trieAtPath))
43-
{
44-
currentTrie = trieAtPath;
45-
}
46-
else
47-
{
48-
var newTrie = new ErrorTrie();
49-
currentTrie[pathSegmentValue] = newTrie;
50-
currentTrie = newTrie;
51-
}
52-
}
53-
54-
currentTrie.Error = error;
55-
}
56-
57-
return root;
11+
return null;
5812
}
5913
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System.Text.Json;
2+
3+
namespace HotChocolate.Fusion.Execution.Clients;
4+
5+
public sealed class SourceSchemaErrors
6+
{
7+
/// <summary>
8+
/// Errors without a path.
9+
/// </summary>
10+
public required List<JsonElement>? RootErrors { get; init; }
11+
12+
public required ErrorTrie Trie { get; init; }
13+
14+
public static SourceSchemaErrors? From(JsonElement json)
15+
{
16+
if (json.ValueKind != JsonValueKind.Array)
17+
{
18+
return null;
19+
}
20+
21+
List<JsonElement>? rootErrors = null;
22+
ErrorTrie root = new ErrorTrie();
23+
24+
foreach (var error in json.EnumerateArray())
25+
{
26+
var currentTrie = root;
27+
28+
if (!error.TryGetProperty("path", out var path) || path.ValueKind != JsonValueKind.Array)
29+
{
30+
rootErrors ??= [];
31+
rootErrors.Add(error);
32+
continue;
33+
}
34+
35+
for (int i = 0, len = path.GetArrayLength(); i < len; ++i)
36+
{
37+
var pathSegment = path[i];
38+
object? pathSegmentValue = pathSegment.ValueKind switch
39+
{
40+
JsonValueKind.String => pathSegment.GetString(),
41+
JsonValueKind.Number => pathSegment.GetInt32(),
42+
_ => null
43+
};
44+
45+
if (pathSegmentValue is null)
46+
{
47+
break;
48+
}
49+
50+
if (currentTrie.TryGetValue(pathSegmentValue, out var trieAtPath))
51+
{
52+
currentTrie = trieAtPath;
53+
}
54+
else
55+
{
56+
var newTrie = new ErrorTrie();
57+
currentTrie[pathSegmentValue] = newTrie;
58+
currentTrie = newTrie;
59+
}
60+
61+
if (i == len - 1)
62+
{
63+
currentTrie.Error = error;
64+
}
65+
}
66+
}
67+
68+
return new SourceSchemaErrors { RootErrors = rootErrors, Trie = root };
69+
}
70+
}

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Clients/SourceSchemaResult.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public SourceSchemaResult(
2020
_resource = resource;
2121
Path = path;
2222
Data = data;
23-
Errors = ErrorTrie.From(errors);
23+
Errors = SourceSchemaErrors.From(errors);
2424
Extensions = extensions;
2525
Final = final;
2626
}
@@ -29,7 +29,7 @@ public SourceSchemaResult(
2929

3030
public JsonElement Data { get; }
3131

32-
public ErrorTrie? Errors { get; }
32+
public SourceSchemaErrors? Errors { get; }
3333

3434
public JsonElement Extensions { get; }
3535

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,17 +144,6 @@ private async Task<ExecutionNodeResult> ExecuteInternalAsync(
144144
try
145145
{
146146
response = await client.ExecuteAsync(context, request, cancellationToken);
147-
148-
if (!response.IsSuccessful)
149-
{
150-
context.AddException();
151-
152-
return new ExecutionNodeResult(
153-
Id,
154-
Activity.Current,
155-
ExecutionStatus.Failed,
156-
Stopwatch.GetElapsedTime(start));
157-
}
158147
}
159148
catch (Exception exception)
160149
{
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System.Text.Json;
2+
using HotChocolate.Language;
3+
4+
namespace HotChocolate.Fusion.Execution;
5+
6+
internal static class ErrorUtils
7+
{
8+
public static ErrorBuilder? CreateErrorBuilder(JsonElement jsonError)
9+
{
10+
if (jsonError.ValueKind is not JsonValueKind.Object)
11+
{
12+
return null;
13+
}
14+
15+
if (jsonError.TryGetProperty("message", out var message)
16+
&& message.ValueKind is JsonValueKind.String)
17+
{
18+
var errorBuilder = ErrorBuilder.New()
19+
.SetMessage(message.GetString()!);
20+
21+
if (jsonError.TryGetProperty("code", out var code)
22+
&& code.ValueKind is JsonValueKind.String)
23+
{
24+
errorBuilder.SetCode(code.GetString());
25+
}
26+
27+
if (jsonError.TryGetProperty("extensions", out var extensions)
28+
&& extensions.ValueKind is JsonValueKind.Object)
29+
{
30+
foreach (var property in extensions.EnumerateObject())
31+
{
32+
errorBuilder.SetExtension(property.Name, property.Value);
33+
}
34+
}
35+
36+
return errorBuilder;
37+
}
38+
39+
return null;
40+
}
41+
}

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/FetchResultStore.cs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public bool AddPartialResults(
8080
nameof(results));
8181
}
8282

83-
var dataElements = ArrayPool<JsonElement>.Shared.Rent(results.Length);
83+
var dataElements = ArrayPool<JsonElement?>.Shared.Rent(results.Length);
8484
var errorTries = ArrayPool<ErrorTrie?>.Shared.Rent(results.Length);
8585
var dataElementsSpan = dataElements.AsSpan()[..results.Length];
8686
var errorTriesSpan = errorTries.AsSpan()[..results.Length];
@@ -97,9 +97,13 @@ public bool AddPartialResults(
9797
// we need to track the result objects as they used rented memory.
9898
_memory.Push(result);
9999

100-
// TODO: This throws for no subgraph data
100+
if (result.Errors?.RootErrors is { } rootErrors)
101+
{
102+
RegisterErrors(_errors, rootErrors);
103+
}
104+
101105
dataElement = GetDataElement(sourcePath, result.Data);
102-
errorTrie = GetErrorTrie(sourcePath, result.Errors);
106+
errorTrie = GetErrorTrie(sourcePath, result.Errors?.Trie);
103107

104108
result = ref Unsafe.Add(ref result, 1)!;
105109
dataElement = ref Unsafe.Add(ref dataElement, 1);
@@ -110,11 +114,31 @@ public bool AddPartialResults(
110114
}
111115
finally
112116
{
113-
ArrayPool<JsonElement>.Shared.Return(dataElements);
117+
ArrayPool<JsonElement?>.Shared.Return(dataElements);
114118
ArrayPool<ErrorTrie?>.Shared.Return(errorTries);
115119
}
116120
}
117121

122+
private static void RegisterErrors(List<IError> errors, List<JsonElement> jsonErrors)
123+
{
124+
var jsonErrorsSpan = CollectionsMarshal.AsSpan(jsonErrors);
125+
ref var jsonError = ref MemoryMarshal.GetReference(jsonErrorsSpan);
126+
ref var end = ref Unsafe.Add(ref jsonError, jsonErrorsSpan.Length);
127+
128+
while (Unsafe.IsAddressLessThan(ref jsonError, ref end))
129+
{
130+
var errorBuilder = ErrorUtils.CreateErrorBuilder(jsonError);
131+
132+
if (errorBuilder is not null)
133+
{
134+
var error = errorBuilder.Build();
135+
errors.Add(error);
136+
}
137+
138+
jsonError = ref Unsafe.Add(ref jsonError, 1)!;
139+
}
140+
}
141+
118142
public void AddPartialResults(ObjectResult result, ReadOnlySpan<Selection> selections)
119143
{
120144
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -149,7 +173,7 @@ public void AddPartialResults(ObjectResult result, ReadOnlySpan<Selection> selec
149173

150174
private bool SaveSafe(
151175
ReadOnlySpan<SourceSchemaResult> results,
152-
ReadOnlySpan<JsonElement> dataElements,
176+
ReadOnlySpan<JsonElement?> dataElements,
153177
ReadOnlySpan<ErrorTrie?> errorTries)
154178
{
155179
_lock.EnterWriteLock();
@@ -381,7 +405,7 @@ public PooledArrayWriter CreateRentedBuffer()
381405
return buffer;
382406
}
383407

384-
private static JsonElement GetDataElement(SelectionPath sourcePath, JsonElement data)
408+
private static JsonElement? GetDataElement(SelectionPath sourcePath, JsonElement data)
385409
{
386410
if (sourcePath.IsRoot)
387411
{
@@ -395,8 +419,7 @@ private static JsonElement GetDataElement(SelectionPath sourcePath, JsonElement
395419
var segment = sourcePath.Segments[i];
396420
if (current.ValueKind != JsonValueKind.Object || !current.TryGetProperty(segment.Name, out current))
397421
{
398-
throw new InvalidOperationException(
399-
$"The path segment '{segment.Name}' does not exist in the data.");
422+
return null;
400423
}
401424
}
402425

src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/ValueCompletion.cs

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,22 @@ public ValueCompletion(
3838

3939
public bool BuildResult(
4040
SelectionSet selectionSet,
41-
JsonElement data,
41+
JsonElement? data,
4242
ErrorTrie? errorTrie,
4343
ObjectResult objectResult)
4444
{
45-
if (data.ValueKind != JsonValueKind.Object)
45+
if (data is not { ValueKind: JsonValueKind.Object })
4646
{
47-
throw new GraphQLException("Expected an object");
47+
if (errorTrie?.GetFirstError() is { } jsonError)
48+
{
49+
var error = ErrorUtils.CreateErrorBuilder(jsonError)?.Build();
50+
if (error is not null)
51+
{
52+
_errors.Add(error);
53+
}
54+
}
55+
56+
return false;
4857
}
4958

5059
foreach (var selection in selectionSet.Selections)
@@ -56,7 +65,7 @@ public bool BuildResult(
5665

5766
var fieldResult = objectResult[selection.ResponseName];
5867

59-
if (data.TryGetProperty(selection.ResponseName, out var element))
68+
if (data.Value.TryGetProperty(selection.ResponseName, out var element))
6069
{
6170
ErrorTrie? errorTrieForResponseName = null;
6271
errorTrie?.TryGetValue(selection.ResponseName, out errorTrieForResponseName);
@@ -260,6 +269,7 @@ private bool TryCompleteObjectValue(
260269
var objectResult = _resultPoolSession.RentObjectResult();
261270

262271
objectResult.Initialize(_resultPoolSession, selectionSet, _includeFlags);
272+
objectResult.SetParent(parent, parent.ParentIndex);
263273

264274
foreach (var field in objectResult.Fields)
265275
{
@@ -302,41 +312,19 @@ private bool TryCompleteAbstractValue(
302312
depth,
303313
parent);
304314

305-
private static IError? CreateErrorFromJson(JsonElement error, Path path, ISyntaxNode syntaxNode)
315+
private static IError? CreateErrorFromJson(JsonElement jsonError, Path path, ISyntaxNode syntaxNode)
306316
{
307-
if (error.ValueKind is not JsonValueKind.Object)
317+
var errorBuilder = ErrorUtils.CreateErrorBuilder(jsonError);
318+
319+
if (errorBuilder is null)
308320
{
309321
return null;
310322
}
311323

312-
if (error.TryGetProperty("message", out var message)
313-
&& message.ValueKind is JsonValueKind.String)
314-
{
315-
var errorBuilder = ErrorBuilder.New()
316-
.SetMessage(message.GetString()!);
317-
318-
if (error.TryGetProperty("code", out var code)
319-
&& code.ValueKind is JsonValueKind.String)
320-
{
321-
errorBuilder.SetCode(code.GetString());
322-
}
323-
324-
if (error.TryGetProperty("extensions", out var extensions)
325-
&& extensions.ValueKind is JsonValueKind.Object)
326-
{
327-
foreach (var property in extensions.EnumerateObject())
328-
{
329-
errorBuilder.SetExtension(property.Name, property.Value);
330-
}
331-
}
332-
333-
errorBuilder.SetPath(path);
334-
errorBuilder.AddLocation(syntaxNode);
335-
336-
return errorBuilder.Build();
337-
}
324+
errorBuilder.SetPath(path);
325+
errorBuilder.AddLocation(syntaxNode);
338326

339-
return null;
327+
return errorBuilder.Build();
340328
}
341329

342330
[MethodImpl(MethodImplOptions.AggressiveInlining)]

0 commit comments

Comments
 (0)