diff --git a/src/CookieCrumble/src/CookieCrumble.HotChocolate/Extensions/SnapshotExtensions.cs b/src/CookieCrumble/src/CookieCrumble.HotChocolate/Extensions/SnapshotExtensions.cs index b81a9c9eda0..238b1ddb904 100644 --- a/src/CookieCrumble/src/CookieCrumble.HotChocolate/Extensions/SnapshotExtensions.cs +++ b/src/CookieCrumble/src/CookieCrumble.HotChocolate/Extensions/SnapshotExtensions.cs @@ -24,34 +24,6 @@ public static void MatchSnapshot( postFix, formatter: CoreFormatters.PlainText); - /// - /// Matches a snapshot of an execution result, merging an incrementally delivered - /// (@defer/@stream) response into its final aggregated form. The snapshot - /// is identical whether the transport delivered the response across several payloads or - /// as a single bundled payload, so it does not depend on delivery timing. - /// - public static void MatchAggregatedSnapshot( - this IExecutionResult? value, - string? postFix = null) - => Snapshot.Match( - value, - postFix, - formatter: SnapshotValueFormatters.ExecutionResultAggregated); - - /// - /// Markdown counterpart of : matches a markdown - /// snapshot of an execution result, merging an incrementally delivered - /// (@defer/@stream) response into its final aggregated form so the snapshot - /// does not depend on whether the transport delivered it incrementally or bundled. - /// - public static void MatchAggregatedMarkdownSnapshot( - this IExecutionResult? value, - object? postFix = null, - string? extension = null) - => Snapshot.Create(postFix?.ToString(), extension) - .Add(value, formatter: SnapshotValueFormatters.ExecutionResultAggregated) - .MatchMarkdown(); - public static Snapshot AddResult( this Snapshot snapshot, IExecutionResult result, diff --git a/src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/ExecutionResultSnapshotValueFormatter.cs b/src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/ExecutionResultSnapshotValueFormatter.cs index 4937768fca0..2bd5aabd618 100644 --- a/src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/ExecutionResultSnapshotValueFormatter.cs +++ b/src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/ExecutionResultSnapshotValueFormatter.cs @@ -4,10 +4,11 @@ using CookieCrumble.Formatters; using HotChocolate; using HotChocolate.Execution; +using static CookieCrumble.HotChocolate.Formatters.StableSnapshotHelpers; namespace CookieCrumble.HotChocolate.Formatters; -internal sealed class ExecutionResultSnapshotValueFormatter(bool alwaysAggregate = false) +internal sealed class ExecutionResultSnapshotValueFormatter : SnapshotValueFormatter { protected override void Format(IBufferWriter snapshot, IExecutionResult value) @@ -18,7 +19,7 @@ protected override void Format(IBufferWriter snapshot, IExecutionResult va } else { - FormatStreamAsync(snapshot, (IResponseStream)value, alwaysAggregate).Wait(); + FormatStreamAsync(snapshot, (IResponseStream)value).Wait(); } } @@ -34,7 +35,7 @@ protected override void FormatMarkdown(IBufferWriter snapshot, IExecutionR { snapshot.Append("```text"); snapshot.AppendLine(); - FormatStreamAsync(snapshot, (IResponseStream)value, alwaysAggregate).Wait(); + FormatStreamAsync(snapshot, (IResponseStream)value).Wait(); } snapshot.AppendLine(); @@ -42,65 +43,239 @@ protected override void FormatMarkdown(IBufferWriter snapshot, IExecutionR snapshot.AppendLine(); } - private static async Task FormatStreamAsync( + private static Task FormatStreamAsync(IBufferWriter snapshot, IResponseStream stream) + // Only @defer/@stream responses carry the incremental-delivery envelope. Other + // streams (subscriptions, batches) are sequences of independent results and are + // written out verbatim, one payload after another. + => stream.Kind is ExecutionResultKind.DeferredResult + ? FormatIncrementalAsync(snapshot, stream) + : FormatEventStreamAsync(snapshot, stream); + + private static async Task FormatEventStreamAsync(IBufferWriter snapshot, IResponseStream stream) + { + await foreach (var result in stream.ReadResultsAsync().ConfigureAwait(false)) + { + snapshot.Append(result.ToJson()); + snapshot.AppendLine(); + } + } + + // Reconstructs a single, delivery-order-independent view of an incrementally + // delivered (@defer/@stream) response: the initial payload's non-protocol + // fields plus the `pending`, `incremental`, and `completed` entries collected + // across every payload and ordered by `id`. The snapshot is identical whether + // the transport bundled the response into one payload or split it across several. + private static async Task FormatIncrementalAsync( IBufferWriter snapshot, - IResponseStream stream, - bool alwaysAggregate) + IResponseStream stream) { - var docs = new List(); - JsonResultPatcher? patcher = null; - var first = true; + var accumulator = new StreamAccumulator(); - try + // StreamAccumulator deep-clones every element it retains, so each parsed + // document is only needed for the duration of its AddPayload call and can be + // disposed immediately afterwards. + await foreach (var result in stream.ReadResultsAsync().ConfigureAwait(false)) { - await foreach (var queryResult in stream.ReadResultsAsync().ConfigureAwait(false)) - { - if (first) - { - first = false; + using var document = JsonDocument.Parse(result.ToJson()); + accumulator.AddPayload(document.RootElement); + } - // When aggregating, the initial payload seeds the patcher regardless of - // whether more payloads follow. This normalizes a response delivered as a - // single bundled payload to the same merged form as an incrementally - // delivered one, so the snapshot is independent of delivery batching. - if (alwaysAggregate || (queryResult.HasNext ?? false)) - { - var doc = JsonDocument.Parse(queryResult.ToJson()); - docs.Add(doc); + await using var writer = new Utf8JsonWriter(snapshot, IndentedWriterOptions); + WriteEnvelope(writer, accumulator); + writer.Flush(); + snapshot.AppendLine(); + } - patcher = new JsonResultPatcher(); - patcher.SetResponse(doc); - continue; - } - } + private static void WriteEnvelope(Utf8JsonWriter writer, StreamAccumulator accumulator) + { + writer.WriteStartObject(); - if (patcher is null) + // The initial payload's non-protocol fields (`data`, `errors`, `extensions`) in + // their original order; the incremental-delivery fields are rebuilt below. + if (accumulator.InitialPayload is { ValueKind: JsonValueKind.Object } initial) + { + foreach (var property in initial.EnumerateObject()) + { + if (IsStreamField(property.Name)) { - snapshot.Append(queryResult.ToJson()); - snapshot.AppendLine(); + continue; } - else - { - var doc = JsonDocument.Parse(queryResult.ToJson()); - docs.Add(doc); - patcher.ApplyPatch(doc); + writer.WritePropertyName(property.Name); + property.Value.WriteTo(writer); + } + } + + WritePending(writer, accumulator.PendingById); + WriteIncremental(writer, accumulator.IncrementalEntries); + WriteCompleted(writer, accumulator.CompletedEntries); + + writer.WriteBoolean("hasNext", false); + + writer.WriteEndObject(); + } + + private static void WritePending( + Utf8JsonWriter writer, + Dictionary pendingById) + { + if (pendingById.Count == 0) + { + return; + } + + var pending = pendingById.Values.ToList(); + pending.Sort(static (x, y) => CompareIds(x.Id, y.Id)); + + writer.WritePropertyName("pending"); + writer.WriteStartArray(); + + foreach (var entry in pending) + { + writer.WriteStartObject(); + writer.WriteString("id", entry.Id); + writer.WritePropertyName("path"); + entry.Path.WriteTo(writer); + + if (!string.IsNullOrEmpty(entry.Label)) + { + writer.WriteString("label", entry.Label); + } + + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + } + + private static void WriteIncremental( + Utf8JsonWriter writer, + List entries) + { + if (entries.Count == 0) + { + return; + } + + // Group by `id` so a stream delivered across several payloads collapses into a + // single entry, independent of how many frames the transport used. + var order = new List(); + var byId = new Dictionary>(); + + foreach (var entry in entries) + { + if (!byId.TryGetValue(entry.Id, out var group)) + { + group = []; + byId.Add(entry.Id, group); + order.Add(entry.Id); + } + + group.Add(entry); + } + + order.Sort(CompareIds); + + writer.WritePropertyName("incremental"); + writer.WriteStartArray(); + + foreach (var id in order) + { + var group = byId[id]; + + writer.WriteStartObject(); + writer.WriteString("id", id); + + if (group.FirstOrDefault(e => e.SubPath is not null)?.SubPath is { } subPath) + { + writer.WritePropertyName("subPath"); + subPath.WriteTo(writer); + } + + if (group.Any(e => e.Items is not null)) + { + writer.WritePropertyName("items"); + writer.WriteStartArray(); + foreach (var entry in group) + { + if (entry.Items is { } items) + { + foreach (var item in items.EnumerateArray()) + { + item.WriteTo(writer); + } + } } + writer.WriteEndArray(); + } + else if (group.FirstOrDefault(e => e.Data is not null)?.Data is { } data) + { + writer.WritePropertyName("data"); + data.WriteTo(writer); } - if (patcher is not null) + WriteMergedErrors(writer, group); + + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + } + + private static void WriteCompleted( + Utf8JsonWriter writer, + List entries) + { + if (entries.Count == 0) + { + return; + } + + var completed = entries.ToList(); + completed.Sort(static (x, y) => CompareIds(x.Id, y.Id)); + + writer.WritePropertyName("completed"); + writer.WriteStartArray(); + + foreach (var entry in completed) + { + writer.WriteStartObject(); + writer.WriteString("id", entry.Id); + + if (entry.Errors is { } errors) { - patcher.WriteResponse(snapshot); - snapshot.AppendLine(); + writer.WritePropertyName("errors"); + errors.WriteTo(writer); } + + writer.WriteEndObject(); } - finally + + writer.WriteEndArray(); + } + + private static void WriteMergedErrors(Utf8JsonWriter writer, List group) + { + if (!group.Any(e => e.Errors is not null)) { - foreach (var doc in docs) + return; + } + + writer.WritePropertyName("errors"); + writer.WriteStartArray(); + + foreach (var entry in group) + { + if (entry.Errors is { } errors) { - doc.Dispose(); + foreach (var error in errors.EnumerateArray()) + { + error.WriteTo(writer); + } } } + + writer.WriteEndArray(); } } diff --git a/src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/SnapshotValueFormatters.cs b/src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/SnapshotValueFormatters.cs index b231a702801..cdea4bbc282 100644 --- a/src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/SnapshotValueFormatters.cs +++ b/src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/SnapshotValueFormatters.cs @@ -10,9 +10,6 @@ public static class SnapshotValueFormatters public static ISnapshotValueFormatter ExecutionResult { get; } = new ExecutionResultSnapshotValueFormatter(); - public static ISnapshotValueFormatter ExecutionResultAggregated { get; } = - new ExecutionResultSnapshotValueFormatter(alwaysAggregate: true); - public static ISnapshotValueFormatter ExecutionResultStable { get; } = new StableExecutionResultSnapshotValueFormatter(); diff --git a/src/HotChocolate/Core/test/Execution.Tests/DeferReferenceTests.cs b/src/HotChocolate/Core/test/Execution.Tests/DeferReferenceTests.cs index 3eff7f17581..5518ebe3bd7 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DeferReferenceTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DeferReferenceTests.cs @@ -34,7 +34,7 @@ ... @defer { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } /// @@ -232,7 +232,7 @@ ... @defer { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } // ======================================================================== @@ -430,7 +430,7 @@ ... @defer { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } /// @@ -513,7 +513,7 @@ ... @defer { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } // ======================================================================== @@ -670,7 +670,7 @@ ... on Hero @defer { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } /// diff --git a/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs b/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs index b001a64cc2a..29513fc1bc5 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs @@ -25,7 +25,7 @@ ... @defer { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -50,7 +50,7 @@ ... @defer { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -72,7 +72,7 @@ ... @defer(label: "abc") { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -149,7 +149,7 @@ fragment Foo on Query { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -176,7 +176,7 @@ ... @defer { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -200,7 +200,7 @@ fragment Foo on Query { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -286,7 +286,7 @@ ... @defer { .Build(), TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -349,7 +349,7 @@ ... @defer { .Build(), TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -382,7 +382,7 @@ ... @defer { .Build(), TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedMarkdownSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } private class StateRequestInterceptor : DefaultHttpRequestInterceptor diff --git a/src/HotChocolate/Core/test/Execution.Tests/StreamTests.cs b/src/HotChocolate/Core/test/Execution.Tests/StreamTests.cs index ab181bb66ee..fd1321b6b30 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/StreamTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/StreamTests.cs @@ -30,7 +30,7 @@ persons @stream { }", TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedSnapshot(); + Assert.IsType(result).MatchSnapshot(); } [Fact] @@ -55,7 +55,7 @@ ... @defer { }", TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedSnapshot(); + Assert.IsType(result).MatchSnapshot(); } [Fact] @@ -76,7 +76,7 @@ persons @stream(initialCount: 1) { }", TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedSnapshot(); + Assert.IsType(result).MatchSnapshot(); } [Fact] @@ -97,7 +97,7 @@ persons @stream(initialCount: 7) { }", TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedSnapshot(); + Assert.IsType(result).MatchSnapshot(); } [Fact] @@ -120,7 +120,7 @@ persons @stream(label: "abc") { """, TestContext.Current.CancellationToken); - Assert.IsType(result).MatchAggregatedSnapshot(); + Assert.IsType(result).MatchSnapshot(); } [Fact] diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Deduplicates_List_Fields_In_Initial_Payload.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Deduplicates_List_Fields_In_Initial_Payload.md index 98b60c5c29b..6cff9811935 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Deduplicates_List_Fields_In_Initial_Payload.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Deduplicates_List_Fields_In_Initial_Payload.md @@ -6,20 +6,75 @@ "hero": { "friends": [ { - "id": "friend-1", - "name": "Han" + "id": "friend-1" }, { - "id": "friend-2", - "name": "Leia" + "id": "friend-2" }, { - "id": "friend-3", - "name": "C-3PO" + "id": "friend-3" } ] } - } + }, + "pending": [ + { + "id": "2", + "path": [ + "hero", + "friends", + 0 + ] + }, + { + "id": "3", + "path": [ + "hero", + "friends", + 1 + ] + }, + { + "id": "4", + "path": [ + "hero", + "friends", + 2 + ] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "name": "Han" + } + }, + { + "id": "3", + "data": { + "name": "Leia" + } + }, + { + "id": "4", + "data": { + "name": "C-3PO" + } + } + ], + "completed": [ + { + "id": "2" + }, + { + "id": "3" + }, + { + "id": "4" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Defer_Fragment_With_Error.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Defer_Fragment_With_Error.md index 9b3863248ec..0170da62e79 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Defer_Fragment_With_Error.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Defer_Fragment_With_Error.md @@ -4,10 +4,40 @@ { "data": { "hero": { - "id": "hero-1", - "errorField": null + "id": "hero-1" } - } + }, + "pending": [ + { + "id": "2", + "path": [ + "hero" + ] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "errorField": null + }, + "errors": [ + { + "message": "resolver error", + "path": [ + "hero", + "errorField" + ] + } + ] + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Defer_Fragment_With_Errors_On_Top_Level_Query_Field.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Defer_Fragment_With_Errors_On_Top_Level_Query_Field.md index e6affe7312b..45715d2b419 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Defer_Fragment_With_Errors_On_Top_Level_Query_Field.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Defer_Fragment_With_Errors_On_Top_Level_Query_Field.md @@ -2,9 +2,39 @@ ```text { - "data": { - "a": null - } + "data": {}, + "pending": [ + { + "id": "2", + "path": [] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "a": null + }, + "errors": [ + { + "message": "Cannot return null for non-nullable field.", + "path": [ + "a", + "nonNullErrorField" + ], + "extensions": { + "code": "HC0018" + } + } + ] + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Emits_Children_Of_Empty_Defer_Fragment.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Emits_Children_Of_Empty_Defer_Fragment.md index 3f6957bcec3..b1180fb7eee 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Emits_Children_Of_Empty_Defer_Fragment.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.Emits_Children_Of_Empty_Defer_Fragment.md @@ -4,10 +4,31 @@ { "data": { "hero": { - "id": "hero-1", - "name": "Luke" + "id": "hero-1" } - } + }, + "pending": [ + { + "id": "2", + "path": [ + "hero" + ] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "name": "Luke" + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.InlineFragment_Defer_With_TypeCondition.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.InlineFragment_Defer_With_TypeCondition.md index ad4fb3ae36c..82c842d9d72 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.InlineFragment_Defer_With_TypeCondition.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferReferenceTests.InlineFragment_Defer_With_TypeCondition.md @@ -4,10 +4,31 @@ { "data": { "hero": { - "id": "hero-1", - "name": "Luke" + "id": "hero-1" } - } + }, + "pending": [ + { + "id": "2", + "path": [ + "hero" + ] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "name": "Luke" + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.md index 6efc6e6bfde..1be6d535c07 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.md @@ -3,10 +3,30 @@ ```text { "data": { - "ensureState": { - "state": "state 123" + "ensureState": {} + }, + "pending": [ + { + "id": "2", + "path": [ + "ensureState" + ] } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "state": "state 123" + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer_2.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer_2.md index d5f10272be4..d500667d0c9 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer_2.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer_2.md @@ -2,11 +2,29 @@ ```text { - "data": { - "ensureState": { - "state": "state 123" + "data": {}, + "pending": [ + { + "id": "2", + "path": [] } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "ensureState": { + "state": "state 123" + } + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.md index 7392a71cb38..60ac60fae53 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.md @@ -2,11 +2,42 @@ ```text { - "data": { - "ensureState": { - "state": "state 123" + "data": {}, + "pending": [ + { + "id": "2", + "path": [] + }, + { + "id": "3", + "path": [ + "ensureState" + ] } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "ensureState": {} + } + }, + { + "id": "3", + "data": { + "state": "state 123" + } + } + ], + "completed": [ + { + "id": "2" + }, + { + "id": "3" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer_2.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer_2.md index 20f6822686e..5759f4f7e96 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer_2.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer_2.md @@ -2,13 +2,58 @@ ```text { - "data": { - "e": { - "more": { + "data": {}, + "pending": [ + { + "id": "2", + "path": [] + }, + { + "id": "3", + "path": [ + "e" + ] + }, + { + "id": "4", + "path": [ + "e", + "more" + ] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "e": {} + } + }, + { + "id": "3", + "data": { + "more": {} + } + }, + { + "id": "4", + "data": { "stuff": "state 123" } } - } + ], + "completed": [ + { + "id": "2" + }, + { + "id": "3" + }, + { + "id": "4" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.md index 6660699b0bb..17ba210f21f 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.md @@ -2,11 +2,29 @@ ```text { - "data": { - "person": { - "id": "UGVyc29uOjE=" + "data": {}, + "pending": [ + { + "id": "2", + "path": [] } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.md index f506c45d484..8f9b491a176 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.md @@ -2,11 +2,30 @@ ```text { - "data": { - "person": { - "id": "UGVyc29uOjE=" + "data": {}, + "pending": [ + { + "id": "2", + "path": [], + "label": "abc" } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.md index 212b0c67482..fe0a14e2dd8 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.md @@ -2,12 +2,44 @@ ```text { - "data": { - "person": { - "id": "UGVyc29uOjE=", - "name": "Pascal" + "data": {}, + "pending": [ + { + "id": "2", + "path": [] + }, + { + "id": "3", + "path": [ + "person" + ] } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } + }, + { + "id": "3", + "data": { + "name": "Pascal" + } + } + ], + "completed": [ + { + "id": "2" + }, + { + "id": "3" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.md index 203b8a7163f..873c24d68d9 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.md @@ -2,11 +2,29 @@ ```text { - "data": { - "person": { - "id": "UGVyc29uOjE=" + "data": {}, + "pending": [ + { + "id": "2", + "path": [] } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.md index 2539516c9f4..78a31aa2471 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.md @@ -2,11 +2,30 @@ ```text { - "data": { - "person": { - "id": "UGVyc29uOjE=" + "data": {}, + "pending": [ + { + "id": "2", + "path": [], + "label": "abc" } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.md index 84c20b20a12..f4c6bd26ca4 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.md +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.md @@ -2,12 +2,44 @@ ```text { - "data": { - "person": { - "id": "UGVyc29uOjE=", - "name": "Pascal" + "data": {}, + "pending": [ + { + "id": "2", + "path": [] + }, + { + "id": "3", + "path": [ + "person" + ] } - } + ], + "incremental": [ + { + "id": "2", + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } + }, + { + "id": "3", + "data": { + "name": "Pascal" + } + } + ], + "completed": [ + { + "id": "2" + }, + { + "id": "3" + } + ], + "hasNext": false } ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream.snap index 4611896538f..4cbfe726e13 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream.snap @@ -13,7 +13,26 @@ { "id": "UGVyc29uOjQ=" } - ], - "wait": true - } + ] + }, + "pending": [ + { + "id": "2", + "path": [] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "wait": true + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_InitialCount_Exceeds_Total_Count.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_InitialCount_Exceeds_Total_Count.snap index 4611896538f..4cbfe726e13 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_InitialCount_Exceeds_Total_Count.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_InitialCount_Exceeds_Total_Count.snap @@ -13,7 +13,26 @@ { "id": "UGVyc29uOjQ=" } - ], - "wait": true - } + ] + }, + "pending": [ + { + "id": "2", + "path": [] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "wait": true + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_InitialCount_Set_To_1.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_InitialCount_Set_To_1.snap index 4611896538f..4cbfe726e13 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_InitialCount_Set_To_1.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_InitialCount_Set_To_1.snap @@ -13,7 +13,26 @@ { "id": "UGVyc29uOjQ=" } - ], - "wait": true - } + ] + }, + "pending": [ + { + "id": "2", + "path": [] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "wait": true + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_Label_Set_To_abc.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_Label_Set_To_abc.snap index 4611896538f..4cbfe726e13 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_Label_Set_To_abc.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_Label_Set_To_abc.snap @@ -13,7 +13,26 @@ { "id": "UGVyc29uOjQ=" } - ], - "wait": true - } + ] + }, + "pending": [ + { + "id": "2", + "path": [] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "wait": true + } + } + ], + "completed": [ + { + "id": "2" + } + ], + "hasNext": false } diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_Nested_Defer.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_Nested_Defer.snap index 0aa5b70503c..e4d80f4b65a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_Nested_Defer.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/StreamTests.Stream_Nested_Defer.snap @@ -2,11 +2,45 @@ "data": { "personNodes": { "nodes": [ - { - "name": "Pascal" - } + {} ] + } + }, + "pending": [ + { + "id": "2", + "path": [] }, - "wait": true - } + { + "id": "3", + "path": [ + "personNodes", + "nodes", + 0 + ] + } + ], + "incremental": [ + { + "id": "2", + "data": { + "wait": true + } + }, + { + "id": "3", + "data": { + "name": "Pascal" + } + } + ], + "completed": [ + { + "id": "2" + }, + { + "id": "3" + } + ], + "hasNext": false }