diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Clients/SourceSchemaResult.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Clients/SourceSchemaResult.cs index 9a6a0a536fd..cd5c4862954 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Clients/SourceSchemaResult.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Clients/SourceSchemaResult.cs @@ -9,6 +9,7 @@ public sealed class SourceSchemaResult : IDisposable private static ReadOnlySpan ExtensionsProperty => "extensions"u8; private readonly SourceResultDocument _document; private readonly bool _ownsDocument; + private bool _errorsParsed; public SourceSchemaResult( Path path, @@ -45,9 +46,20 @@ public SourceResultElement Data } public SourceSchemaErrors? Errors - => _document.Root.TryGetProperty(ErrorsProperty, out var errors) - ? SourceSchemaErrors.From(errors) - : null; + { + get + { + if (!_errorsParsed) + { + field = _document.Root.TryGetProperty(ErrorsProperty, out var errors) + ? SourceSchemaErrors.From(errors) + : null; + _errorsParsed = true; + } + + return field; + } + } internal SourceResultElement RawErrors { diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs index 28278844185..914f86eb23d 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/OperationExecutionNode.cs @@ -147,7 +147,7 @@ protected override async ValueTask OnExecuteAsync( { buffer[index++] = result; - if (result.Errors is not null) + if (result.HasErrors) { hasSomeErrors = true; } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/FetchResultStore.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/FetchResultStore.cs index 6cbd4cab36a..0b7cc9db9d2 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/FetchResultStore.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Results/FetchResultStore.cs @@ -113,14 +113,16 @@ public bool AddPartialResults( // we need to track the result objects as they used rented memory. _memory.Push(result); - if (result.Errors?.RootErrors is { Length: > 0 } rootErrors) + var errors = result.Errors; + + if (errors?.RootErrors is { Length: > 0 } rootErrors) { _errors ??= []; _errors.AddRange(rootErrors); } dataElement = GetDataElement(sourcePath, result.Data); - errorTrie = GetErrorTrie(sourcePath, result.Errors?.Trie); + errorTrie = GetErrorTrie(sourcePath, errors?.Trie); result = ref Unsafe.Add(ref result, 1)!; dataElement = ref Unsafe.Add(ref dataElement, 1); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Text.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Text.cs index f912afa58af..db3f54018ae 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Text.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Text.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Unicode; using HotChocolate.Text.Json; @@ -157,12 +158,7 @@ internal ReadOnlySpan GetRawValue(Cursor cursor, bool includeQuotes) var start = row.Location; var endCursor = GetEndIndex(cursor, includeEndElement: false); var endRow = _parsedData.Get(endCursor); - var endRowLength = endRow.SizeOrLength; - - if (endRow.TokenType is JsonTokenType.EndObject or JsonTokenType.StartArray) - { - endRowLength = 1; - } + var endRowLength = GetEndRowLength(endRow); return ReadRawValue(start, endRow.Location - start + endRowLength); } @@ -188,12 +184,7 @@ internal ReadOnlyMemory GetRawValueAsMemory(Cursor cursor, bool includeQuo var start = row.Location; var endCursor = GetEndIndex(cursor, includeEndElement: false); var endRow = _parsedData.Get(endCursor); - var endRowLength = endRow.SizeOrLength; - - if (endRow.TokenType is JsonTokenType.EndObject or JsonTokenType.StartArray) - { - endRowLength = 1; - } + var endRowLength = GetEndRowLength(endRow); return ReadRawValueAsMemory(start, endRow.Location - start + endRowLength); } @@ -219,12 +210,7 @@ internal ValueRange GetRawValuePointer(Cursor cursor, bool includeQuotes) var start = row.Location; var endCursor = GetEndIndex(cursor, includeEndElement: false); var endRow = _parsedData.Get(endCursor); - var endRowLength = endRow.SizeOrLength; - - if (endRow.TokenType is JsonTokenType.EndObject or JsonTokenType.StartArray) - { - endRowLength = 1; - } + var endRowLength = GetEndRowLength(endRow); return new ValueRange(start, endRow.Location - start + endRowLength); } @@ -256,7 +242,7 @@ private ReadOnlySpan GetPropertyRawValue(Cursor valueCursor) var endCursor = GetEndIndex(valueCursor, includeEndElement: false); var endRow = _parsedData.Get(endCursor); - var endOffset = endRow.Location + endRow.SizeOrLength; + var endOffset = endRow.Location + GetEndRowLength(endRow); return ReadRawValue(start, endOffset - start); } @@ -282,4 +268,10 @@ internal Cursor GetEndIndex(Cursor cursor, bool includeEndElement) return endId; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int GetEndRowLength(DbRow endRow) + => endRow.TokenType is JsonTokenType.EndObject or JsonTokenType.EndArray + ? 1 + : endRow.SizeOrLength; } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs index dff90f32564..a916938c3a9 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs @@ -1,4 +1,5 @@ using System.Buffers; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; @@ -252,4 +253,30 @@ public void Dispose() _disposed = true; } } + + public override string ToString() + { + if (_usedChunks == 0) + { + return string.Empty; + } + + var totalSize = 0; + + for (var i = 0; i < _usedChunks; i++) + { + totalSize += _dataChunks[i].Length; + } + + var buffer = new byte[totalSize]; + var offset = 0; + + for (var i = 0; i < _usedChunks; i++) + { + _dataChunks[i].CopyTo(buffer, offset); + offset += _dataChunks[i].Length; + } + + return s_utf8Encoding.GetString(buffer).TrimEnd('\0'); + } } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultElement.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultElement.cs index 7b1a1e53b48..b9867d38b7c 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultElement.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultElement.cs @@ -6,6 +6,7 @@ namespace HotChocolate.Fusion.Text.Json; +[DebuggerDisplay("{DebuggerDisplay,nq}")] public readonly partial struct SourceResultElement { internal readonly SourceResultDocument _parent; @@ -582,6 +583,43 @@ public ObjectEnumerator EnumerateObject() return new ObjectEnumerator(this); } + /// + public override string ToString() + { + switch (TokenType) + { + case JsonTokenType.None: + case JsonTokenType.Null: + return string.Empty; + + case JsonTokenType.True: + return bool.TrueString; + + case JsonTokenType.False: + return bool.FalseString; + + case JsonTokenType.Number: + case JsonTokenType.StartArray: + case JsonTokenType.StartObject: + Debug.Assert(_parent != null); + return _parent.GetRawValueAsString(_cursor); + + case JsonTokenType.String: + return GetString()!; + + case JsonTokenType.Comment: + case JsonTokenType.EndArray: + case JsonTokenType.EndObject: + default: + Debug.Fail($"No handler for {nameof(JsonTokenType)}.{TokenType}"); + return string.Empty; + } + } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay + => ValueKind == JsonValueKind.Undefined ? "" : ToString(); + private void CheckValidInstance() { if (_parent == null) diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultProperty.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultProperty.cs index 904f5230505..02a9b7439c1 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultProperty.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultProperty.cs @@ -96,5 +96,5 @@ internal bool EscapedNameEquals(ReadOnlySpan utf8Text) [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay - => Value.ValueKind == JsonValueKind.Undefined ? "" : $"\"{ToString()}\""; + => Value.ValueKind == JsonValueKind.Undefined ? "" : ToString(); } diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/SourceSchemaErrorTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/SourceSchemaErrorTests.cs index d85fbf88573..31302778e50 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/SourceSchemaErrorTests.cs +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/SourceSchemaErrorTests.cs @@ -1047,6 +1047,11 @@ public string SomeField(IResolverContext context) ErrorBuilder.New() .SetMessage("Something went wrong") .SetCode("SOME_ERROR") + .SetExtension("stringValue", "a-string") + .SetExtension("booleanValue", true) + .SetExtension("numberValue", 123) + .SetExtension("arrayValue", new [] { 1, 2, 3}) + .SetExtension("emptyArrayValue", Array.Empty()) .SetPath(context.Path) .SetException(new Exception("Some exception")) .Build()); diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_Extensions_From_Source_Schema_Are_Properly_Forwarded.yaml b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_Extensions_From_Source_Schema_Are_Properly_Forwarded.yaml index ffe10aea87b..7150f30d4ba 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_Extensions_From_Source_Schema_Are_Properly_Forwarded.yaml +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.AspNetCore.Tests/__snapshots__/SourceSchemaErrorTests.Error_Extensions_From_Source_Schema_Are_Properly_Forwarded.yaml @@ -15,6 +15,15 @@ response: ], "extensions": { "code": "SOME_ERROR", + "stringValue": "a-string", + "booleanValue": true, + "numberValue": 123, + "arrayValue": [ + 1, + 2, + 3 + ], + "emptyArrayValue": [], "exception": { "message": "Some exception", "stackTrace": null @@ -51,6 +60,15 @@ sourceSchemas: ], "extensions": { "code": "SOME_ERROR", + "stringValue": "a-string", + "booleanValue": true, + "numberValue": 123, + "arrayValue": [ + 1, + 2, + 3 + ], + "emptyArrayValue": [], "exception": { "message": "Some exception", "stackTrace": null diff --git a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Text/Json/SourceResultDocumentTests.cs b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Text/Json/SourceResultDocumentTests.cs index 6542534705c..422ddcccde8 100644 --- a/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Text/Json/SourceResultDocumentTests.cs +++ b/src/HotChocolate/Fusion-vnext/test/Fusion.Execution.Tests/Text/Json/SourceResultDocumentTests.cs @@ -798,4 +798,18 @@ public void GetRawText_ReturnsOriginalJson_Success() Assert.Equal("\"hello\"", result.Root.GetProperty("string").GetRawText()); Assert.Contains("nested", result.Root.GetProperty("object").GetRawText()); } + + [Fact] + public void GetRawText_Array_Success() + { + var json = "{\"arr\":[1,2,3],\"next\":4}"u8.ToArray(); + var chunk = new byte[128 * 1024]; + json.AsSpan().CopyTo(chunk); + + var result = SourceResultDocument.Parse([chunk], json.Length, 1, pooledMemory: false); + + var array = result.Root.GetProperty("arr"); + Assert.Equal("[1,2,3]", array.GetRawText()); + Assert.Equal("[1,2,3]", Encoding.UTF8.GetString(array.GetRawValueAsMemory().Span)); + } }