Skip to content

Commit 103ee4c

Browse files
authored
Implements the new @defer draft spec (#9095)
1 parent d6466f3 commit 103ee4c

File tree

353 files changed

+8630
-3312
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

353 files changed

+8630
-3312
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using HotChocolate.Language;
2+
using SnapshotValueFormatters = CookieCrumble.HotChocolate.Language.Formatters.SnapshotValueFormatters;
3+
4+
namespace CookieCrumble.HotChocolate;
5+
6+
public static class SnapshotExtensions
7+
{
8+
public static void MatchSnapshot(
9+
this ISyntaxNode? value,
10+
string? postFix = null)
11+
=> Snapshot.Match(
12+
value,
13+
postFix,
14+
extension: ".graphql",
15+
formatter: SnapshotValueFormatters.GraphQLSyntaxNode);
16+
}

src/CookieCrumble/src/CookieCrumble.HotChocolate.Language/Formatters/GraphQLSnapshotValueFormatter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ internal sealed class GraphQLSyntaxNodeSnapshotValueFormatter : SnapshotValueFor
99
{
1010
protected override void Format(IBufferWriter<byte> snapshot, ISyntaxNode value)
1111
{
12-
var serialized = value.Print().AsSpan();
12+
var serialized = value.Print(indented: true).AsSpan();
1313
var buffer = ArrayPool<char>.Shared.Rent(serialized.Length);
1414
var span = buffer.AsSpan()[..serialized.Length];
1515
var written = 0;

src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumbleHotChocolate.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ public sealed class CookieCrumbleHotChocolate : SnapshotModule
88
protected override IEnumerable<ISnapshotValueFormatter> CreateFormatters()
99
{
1010
yield return SnapshotValueFormatters.ExecutionResult;
11-
yield return SnapshotValueFormatters.GraphQL;
1211
yield return SnapshotValueFormatters.GraphQLHttp;
1312
yield return SnapshotValueFormatters.OperationResult;
1413
yield return SnapshotValueFormatters.Schema;
1514
yield return SnapshotValueFormatters.SchemaError;
1615
yield return SnapshotValueFormatters.Error;
16+
yield return SnapshotValueFormatters.ErrorList;
1717
yield return SnapshotValueFormatters.ResultElement;
1818
}
1919
}

src/CookieCrumble/src/CookieCrumble.HotChocolate/Extensions/SnapshotExtensions.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
using CookieCrumble.HotChocolate.Formatters;
22
using HotChocolate;
33
using HotChocolate.Execution;
4-
using HotChocolate.Language;
54
using CoreFormatters = CookieCrumble.Formatters.SnapshotValueFormatters;
65

76
namespace CookieCrumble.HotChocolate;
87

98
public static class SnapshotExtensions
109
{
11-
public static void MatchSnapshot(
12-
this ISyntaxNode? value,
13-
string? postFix = null)
14-
=> Snapshot.Match(
15-
value,
16-
postFix,
17-
extension: ".graphql",
18-
formatter: SnapshotValueFormatters.GraphQL);
19-
2010
public static void MatchSnapshot(
2111
this ISchemaDefinition? value,
2212
string? postFix = null)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Buffers;
2+
using System.Text.Encodings.Web;
3+
using System.Text.Json;
4+
using CookieCrumble.Formatters;
5+
using HotChocolate;
6+
using HotChocolate.Execution;
7+
using HotChocolate.Text.Json;
8+
9+
namespace CookieCrumble.HotChocolate.Formatters;
10+
11+
internal sealed class ErrorListSnapshotValueFormatter
12+
: SnapshotValueFormatter<IReadOnlyList<IError>>
13+
{
14+
protected override void Format(IBufferWriter<byte> snapshot, IReadOnlyList<IError> value)
15+
{
16+
var writerOptions = new JsonWriterOptions
17+
{
18+
Indented = true,
19+
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
20+
};
21+
22+
var serializationOptions = new JsonSerializerOptions
23+
{
24+
WriteIndented = true
25+
};
26+
27+
var writer = new JsonWriter(snapshot, writerOptions);
28+
JsonValueFormatter.WriteErrors(writer, value, serializationOptions);
29+
}
30+
}

src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/ErrorSnapshotValueFormatter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ internal sealed class ErrorSnapshotValueFormatter()
1313
protected override void Format(IBufferWriter<byte> snapshot, IError value)
1414
{
1515
var jsonWriter = new JsonWriter(snapshot, new JsonWriterOptions { Indented = true });
16-
JsonValueFormatter.WriteError(jsonWriter, value, new JsonSerializerOptions { WriteIndented = true }, default);
16+
JsonValueFormatter.WriteError(jsonWriter, value, new JsonSerializerOptions { WriteIndented = true });
1717
}
1818
}

src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/ExecutionResultSnapshotValueFormatter.cs

Lines changed: 76 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -101,163 +101,138 @@ private static async Task FormatStreamAsync(
101101

102102
internal sealed class JsonResultPatcher
103103
{
104-
private const string Data = "data";
105-
private const string Items = "items";
106-
private const string Incremental = "incremental";
107-
private const string Path = "path";
104+
private const string DataProp = "data";
105+
private const string ItemsProp = "items";
106+
private const string IncrementalProp = "incremental";
107+
private const string PendingProp = "pending";
108+
private const string PathProp = "path";
109+
private const string SubPathProp = "subPath";
110+
private const string IdProp = "id";
108111
private JsonObject? _json;
112+
private readonly Dictionary<string, JsonElement> _pendingPaths = new();
109113

110114
public void SetResponse(JsonDocument response)
111115
{
112116
ArgumentNullException.ThrowIfNull(response);
113117

114118
_json = JsonObject.Create(response.RootElement);
119+
ProcessPayload(response.RootElement);
115120
}
116121

117-
public void WriteResponse(IBufferWriter<byte> snapshot)
122+
public void ApplyPatch(JsonDocument patch)
118123
{
119124
if (_json is null)
120125
{
121126
throw new InvalidOperationException(
122127
"You must first set the initial response before you can apply patches.");
123128
}
124129

125-
using var writer = new Utf8JsonWriter(snapshot, new JsonWriterOptions { Indented = true });
126-
127-
_json.Remove("hasNext");
128-
129-
_json.WriteTo(writer);
130-
writer.Flush();
130+
ProcessPayload(patch.RootElement);
131131
}
132132

133-
public void ApplyPatch(JsonDocument patch)
133+
public void WriteResponse(IBufferWriter<byte> snapshot)
134134
{
135135
if (_json is null)
136136
{
137137
throw new InvalidOperationException(
138138
"You must first set the initial response before you can apply patches.");
139139
}
140140

141-
if (!patch.RootElement.TryGetProperty(Incremental, out var incremental))
142-
{
143-
throw new ArgumentException("A patch result must contain a property `incremental`.");
144-
}
141+
using var writer = new Utf8JsonWriter(snapshot, new JsonWriterOptions { Indented = true });
145142

146-
foreach (var element in incremental.EnumerateArray())
147-
{
148-
if (element.TryGetProperty(Data, out var data))
149-
{
150-
PatchIncrementalData(element, JsonObject.Create(data)!);
151-
}
152-
else if (element.TryGetProperty(Items, out var items))
153-
{
154-
PatchIncrementalItems(element, JsonArray.Create(items)!);
155-
}
156-
}
157-
}
143+
_json.Remove("hasNext");
144+
_json.Remove("pending");
145+
_json.Remove("incremental");
146+
_json.Remove("completed");
158147

159-
private void PatchIncrementalData(JsonElement incremental, JsonObject data)
160-
{
161-
if (incremental.TryGetProperty(Path, out var pathProp))
162-
{
163-
var (current, last) = SelectNodeToPatch(_json![Data]!, pathProp);
164-
ApplyPatch(current, last, data);
165-
}
148+
_json.WriteTo(writer);
149+
writer.Flush();
166150
}
167151

168-
private void PatchIncrementalItems(JsonElement incremental, JsonArray items)
152+
private void ProcessPayload(JsonElement root)
169153
{
170-
if (incremental.TryGetProperty(Path, out var pathProp))
154+
if (root.TryGetProperty(PendingProp, out var pending))
171155
{
172-
var (current, last) = SelectNodeToPatch(_json![Data]!, pathProp);
173-
var i = last.GetInt32();
174-
var target = current.AsArray();
175-
176-
while (items.Count > 0)
156+
foreach (var entry in pending.EnumerateArray())
177157
{
178-
var item = items[0];
179-
items.RemoveAt(0);
180-
target.Insert(i++, item);
158+
if (entry.TryGetProperty(IdProp, out var id)
159+
&& entry.TryGetProperty(PathProp, out var path))
160+
{
161+
_pendingPaths[id.GetString()!] = path.Clone();
162+
}
181163
}
182164
}
183-
}
184165

185-
private static void ApplyPatch(JsonNode current, JsonElement last, JsonObject patchData)
186-
{
187-
if (last.ValueKind is JsonValueKind.Undefined)
166+
if (root.TryGetProperty(IncrementalProp, out var incremental))
188167
{
189-
foreach (var prop in patchData.ToArray())
168+
foreach (var element in incremental.EnumerateArray())
190169
{
191-
patchData.Remove(prop.Key);
192-
current[prop.Key] = prop.Value;
193-
}
194-
}
195-
else if (last.ValueKind is JsonValueKind.String)
196-
{
197-
current = current[last.GetString()!]!;
170+
if (!element.TryGetProperty(IdProp, out var idElement))
171+
{
172+
continue;
173+
}
198174

199-
foreach (var prop in patchData.ToArray())
200-
{
201-
patchData.Remove(prop.Key);
202-
current[prop.Key] = prop.Value;
203-
}
204-
}
205-
else if (last.ValueKind is JsonValueKind.Number)
206-
{
207-
var index = last.GetInt32();
208-
var element = current[index];
175+
var id = idElement.GetString()!;
209176

210-
if (element is null)
211-
{
212-
current[index] = patchData;
213-
}
214-
else
215-
{
216-
foreach (var prop in patchData.ToArray())
177+
if (!_pendingPaths.TryGetValue(id, out var basePath))
178+
{
179+
continue;
180+
}
181+
182+
if (element.TryGetProperty(DataProp, out var data))
183+
{
184+
PatchData(basePath, element, JsonObject.Create(data)!);
185+
}
186+
else if (element.TryGetProperty(ItemsProp, out var items))
217187
{
218-
patchData.Remove(prop.Key);
219-
element[prop.Key] = prop.Value;
188+
PatchItems(basePath, JsonArray.Create(items)!);
220189
}
221190
}
222191
}
223-
else
192+
}
193+
194+
private void PatchData(JsonElement basePath, JsonElement incremental, JsonObject data)
195+
{
196+
var current = NavigatePath(_json![DataProp]!, basePath);
197+
198+
if (incremental.TryGetProperty(SubPathProp, out var subPath))
224199
{
225-
throw new NotSupportedException("Path segment must be int or string.");
200+
current = NavigatePath(current, subPath);
201+
}
202+
203+
foreach (var prop in data.ToArray())
204+
{
205+
data.Remove(prop.Key);
206+
current[prop.Key] = prop.Value;
226207
}
227208
}
228209

229-
private static (JsonNode Node, JsonElement PathSegment) SelectNodeToPatch(
230-
JsonNode root,
231-
JsonElement path)
210+
private void PatchItems(JsonElement basePath, JsonArray items)
232211
{
233-
if (path.GetArrayLength() == 0)
212+
var target = NavigatePath(_json![DataProp]!, basePath).AsArray();
213+
214+
while (items.Count > 0)
234215
{
235-
return (root, default);
216+
var item = items[0];
217+
items.RemoveAt(0);
218+
target.Add(item);
236219
}
220+
}
237221

222+
private static JsonNode NavigatePath(JsonNode root, JsonElement path)
223+
{
238224
var current = root;
239-
JsonElement? last = null;
240225

241-
foreach (var element in path.EnumerateArray())
226+
foreach (var segment in path.EnumerateArray())
242227
{
243-
if (last is not null)
228+
current = segment.ValueKind switch
244229
{
245-
current = last.Value.ValueKind switch
246-
{
247-
JsonValueKind.String => current[last.Value.GetString()!]!,
248-
JsonValueKind.Number => current[last.Value.GetInt32()]!,
249-
_ => throw new NotSupportedException("Path segment must be int or string.")
250-
};
251-
}
252-
253-
last = element;
254-
}
255-
256-
if (current is null || last is null)
257-
{
258-
throw new InvalidOperationException("Patch had invalid structure.");
230+
JsonValueKind.String => current[segment.GetString()!]!,
231+
JsonValueKind.Number => current[segment.GetInt32()]!,
232+
_ => throw new NotSupportedException("Path segment must be int or string.")
233+
};
259234
}
260235

261-
return (current, last.Value);
236+
return current;
262237
}
263238
}

src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/GraphQLSnapshotValueFormatter.cs

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/CookieCrumble/src/CookieCrumble.HotChocolate/Formatters/ResultElementSnapshotValueFormatter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ internal sealed class ResultElementSnapshotValueFormatter
88
: SnapshotValueFormatter<ResultElement>
99
{
1010
protected override void Format(IBufferWriter<byte> snapshot, ResultElement element)
11-
=> element.WriteTo(snapshot, indented: true);
11+
=> element.WriteTo(snapshot, indented: true);
1212
}

0 commit comments

Comments
 (0)