Skip to content

Commit ab3909e

Browse files
Convert Fusion Snapshots to Markdown (#6770)
1 parent 9479306 commit ab3909e

File tree

125 files changed

+3027
-2113
lines changed

Some content is hidden

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

125 files changed

+3027
-2113
lines changed

src/CookieCrumble/src/CookieCrumble/CookieCrumble.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0' OR '$(TargetFramework)' == 'net8.0'">
3838
<ProjectReference Include="..\..\..\HotChocolate\Fusion\src\Core\HotChocolate.Fusion.csproj" />
39+
<ProjectReference Include="..\..\..\HotChocolate\Skimmed\src\Skimmed\Skimmed.csproj" />
3940
</ItemGroup>
4041

4142
</Project>

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public static void MatchInlineSnapshot(
1515

1616
public static void MatchSnapshot(this Snapshot value)
1717
=> value.Match();
18-
18+
1919
public static void MatchSnapshot(
2020
this object? value,
2121
object? postFix = null,
@@ -30,6 +30,9 @@ public static void MatchMarkdownSnapshot(
3030
ISnapshotValueFormatter? formatter = null)
3131
=> Snapshot.Create(postFix?.ToString(), extension).Add(value, formatter: formatter).MatchMarkdown();
3232

33+
public static void MatchMarkdownSnapshot(this Snapshot value)
34+
=> value.MatchMarkdown();
35+
3336
public static void MatchSnapshot(
3437
this ISyntaxNode? value,
3538
string? postFix = null)

src/CookieCrumble/src/CookieCrumble/Formatters/JsonElementSnapshotValueFormatter.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
namespace CookieCrumble.Formatters;
66

7-
internal sealed class JsonElementSnapshotValueFormatter : SnapshotValueFormatter<JsonElement>
7+
internal sealed class JsonElementSnapshotValueFormatter() : SnapshotValueFormatter<JsonElement>("json")
88
{
99
private readonly JsonSerializerOptions _options =
1010
new(JsonSerializerDefaults.Web)
1111
{
12-
WriteIndented = true,
12+
WriteIndented = true,
1313
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
1414
};
1515

@@ -20,14 +20,4 @@ protected override void Format(IBufferWriter<byte> snapshot, JsonElement value)
2020
buffer.AsSpan().CopyTo(span);
2121
snapshot.Advance(buffer.Length);
2222
}
23-
24-
protected override void FormatMarkdown(IBufferWriter<byte> snapshot, JsonElement value)
25-
{
26-
snapshot.Append("```json");
27-
snapshot.AppendLine();
28-
Format(snapshot, value);
29-
snapshot.AppendLine();
30-
snapshot.Append("```");
31-
snapshot.AppendLine();
32-
}
33-
}
23+
}

src/CookieCrumble/src/CookieCrumble/Formatters/QueryPlanSnapshotValueFormatter.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44

55
namespace CookieCrumble.Formatters;
66

7-
internal sealed class QueryPlanSnapshotValueFormatter : SnapshotValueFormatter<QueryPlan>
7+
internal sealed class QueryPlanSnapshotValueFormatter() : SnapshotValueFormatter<QueryPlan>("json")
88
{
99
protected override void Format(IBufferWriter<byte> snapshot, QueryPlan value)
10-
{
11-
value.Format(snapshot);
12-
}
10+
=> value.Format(snapshot);
1311
}
1412
#endif

src/CookieCrumble/src/CookieCrumble/Formatters/SchemaSnapshotValueFormatter.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,8 @@
33

44
namespace CookieCrumble.Formatters;
55

6-
internal sealed class SchemaSnapshotValueFormatter : SnapshotValueFormatter<ISchema>
6+
internal sealed class SchemaSnapshotValueFormatter() : SnapshotValueFormatter<ISchema>("graphql")
77
{
88
protected override void Format(IBufferWriter<byte> snapshot, ISchema value)
99
=> snapshot.Append(value.ToString());
10-
11-
protected override void FormatMarkdown(IBufferWriter<byte> snapshot, ISchema value)
12-
{
13-
snapshot.Append("```graphql");
14-
snapshot.AppendLine();
15-
Format(snapshot, value);
16-
snapshot.AppendLine();
17-
snapshot.Append("```");
18-
snapshot.AppendLine();
19-
}
2010
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#if NET7_0_OR_GREATER
2+
using System.Buffers;
3+
using HotChocolate.Skimmed;
4+
using HotChocolate.Skimmed.Serialization;
5+
6+
namespace CookieCrumble.Formatters;
7+
8+
internal sealed class SkimmedSchemaSnapshotValueFormatter() : SnapshotValueFormatter<Schema>("graphql")
9+
{
10+
protected override void Format(IBufferWriter<byte> snapshot, Schema value)
11+
=> snapshot.Append(SchemaFormatter.FormatAsString(value));
12+
}
13+
#endif

src/CookieCrumble/src/CookieCrumble/Formatters/SnapshotValueFormatter.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ namespace CookieCrumble.Formatters;
55
/// <summary>
66
/// Formats a snapshot segment value for the snapshot file.
77
/// </summary>
8-
public abstract class SnapshotValueFormatter<TValue>
9-
: ISnapshotValueFormatter
8+
public abstract class SnapshotValueFormatter<TValue>(string markdownKind = "text") : ISnapshotValueFormatter
109
, IMarkdownSnapshotValueFormatter
1110
{
1211
public bool CanHandle(object? value)
@@ -17,15 +16,15 @@ protected virtual bool CanHandle(TValue? value)
1716

1817
public void Format(IBufferWriter<byte> snapshot, object? value)
1918
=> Format(snapshot, (TValue)value!);
20-
19+
2120
public void FormatMarkdown(IBufferWriter<byte> snapshot, object? value)
2221
=> FormatMarkdown(snapshot, (TValue)value!);
2322

2423
protected abstract void Format(IBufferWriter<byte> snapshot, TValue value);
2524

2625
protected virtual void FormatMarkdown(IBufferWriter<byte> snapshot, TValue value)
2726
{
28-
snapshot.Append("```text");
27+
snapshot.Append($"```{markdownKind}");
2928
snapshot.AppendLine();
3029
Format(snapshot, value);
3130
snapshot.AppendLine();

src/CookieCrumble/src/CookieCrumble/Snapshot.cs

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public class Snapshot
3333
new JsonElementSnapshotValueFormatter(),
3434
#if NET7_0_OR_GREATER
3535
new QueryPlanSnapshotValueFormatter(),
36+
new SkimmedSchemaSnapshotValueFormatter(),
3637
#endif
3738
});
3839
private static readonly JsonSnapshotValueFormatter _defaultFormatter = new();
@@ -54,7 +55,7 @@ public Snapshot(string? postFix = null, string? extension = null)
5455

5556
public static Snapshot Create(string? postFix = null, string? extension = null)
5657
=> new(postFix, extension);
57-
58+
5859
public static DisposableSnapshot Start(string? postFix = null, string? extension = null)
5960
=> new(postFix, extension);
6061

@@ -257,15 +258,52 @@ public void Match()
257258
}
258259
}
259260
}
260-
261+
262+
public async ValueTask MatchMarkdownAsync(CancellationToken cancellationToken = default)
263+
{
264+
var writer = new ArrayBufferWriter<byte>();
265+
266+
writer.Append($"# {_title}");
267+
writer.AppendLine();
268+
writer.AppendLine();
269+
270+
WriteMarkdownSegments(writer);
271+
272+
var snapshotFile = Combine(CreateSnapshotDirectoryName(), CreateMarkdownSnapshotFileName());
273+
274+
if (!File.Exists(snapshotFile))
275+
{
276+
EnsureDirectoryExists(snapshotFile);
277+
await using var stream = File.Create(snapshotFile);
278+
await stream.WriteAsync(writer.WrittenMemory, cancellationToken);
279+
}
280+
else
281+
{
282+
var mismatchFile = Combine(CreateMismatchDirectoryName(), CreateMarkdownSnapshotFileName());
283+
EnsureFileDoesNotExist(mismatchFile);
284+
var before = await File.ReadAllTextAsync(snapshotFile, cancellationToken);
285+
var after = _encoding.GetString(writer.WrittenSpan);
286+
287+
if (MatchSnapshot(before, after, false, out var diff))
288+
{
289+
return;
290+
}
291+
292+
EnsureDirectoryExists(mismatchFile);
293+
await using var stream = File.Create(mismatchFile);
294+
await stream.WriteAsync(writer.WrittenMemory, cancellationToken);
295+
throw new Xunit.Sdk.XunitException(diff);
296+
}
297+
}
298+
261299
public void MatchMarkdown()
262300
{
263301
var writer = new ArrayBufferWriter<byte>();
264-
302+
265303
writer.Append($"# {_title}");
266304
writer.AppendLine();
267305
writer.AppendLine();
268-
306+
269307
WriteMarkdownSegments(writer);
270308

271309
var snapshotFile = Combine(CreateSnapshotDirectoryName(), CreateMarkdownSnapshotFileName());
@@ -287,7 +325,7 @@ public void MatchMarkdown()
287325
{
288326
return;
289327
}
290-
328+
291329
EnsureDirectoryExists(mismatchFile);
292330
using var stream = File.Create(mismatchFile);
293331
stream.Write(writer.WrittenSpan);
@@ -344,7 +382,7 @@ private void WriteSegments(IBufferWriter<byte> writer)
344382
next = true;
345383
}
346384
}
347-
385+
348386
private void WriteMarkdownSegments(IBufferWriter<byte> writer)
349387
{
350388
if (_segments.Count == 1)
@@ -488,11 +526,11 @@ private string CreateSnapshotFileName()
488526
? string.Concat(fileName, _extension)
489527
: string.Concat(fileName, "_", _postFix, _extension);
490528
}
491-
529+
492530
private string CreateMarkdownSnapshotFileName()
493531
{
494532
var extension = _extension.EqualsOrdinal(".snap") ? ".md" : _extension;
495-
533+
496534
var fileName = GetFileNameWithoutExtension(_fileName);
497535

498536
return string.IsNullOrEmpty(_postFix)
@@ -532,7 +570,7 @@ private static string CreateFileName(StackFrame[] frames)
532570
"get the snapshot name, then reach this name to your " +
533571
"Snapshot.Match method.");
534572
}
535-
573+
536574
private static string CreateMarkdownTitle(StackFrame[] frames)
537575
{
538576
foreach (var stackFrame in frames)
@@ -618,4 +656,4 @@ public sealed class DisposableSnapshot(string? postFix = null, string? extension
618656
, IDisposable
619657
{
620658
public void Dispose() => Match();
621-
}
659+
}

src/HotChocolate/Fusion/test/CommandLine.Tests/ComposeCommandTests.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ await PackageHelper.CreateSubgraphPackageAsync(
5252
snapshot.Add(subgraph, $"{subgraph.Name} Subgraph Configuration");
5353
}
5454

55-
snapshot.MatchSnapshot();
55+
await snapshot.MatchMarkdownAsync();
5656
}
5757

5858
[Fact]
@@ -111,7 +111,7 @@ await app.InvokeAsync(
111111
snapshot.Add(subgraph, $"{subgraph.Name} Subgraph Configuration");
112112
}
113113

114-
snapshot.MatchSnapshot();
114+
await snapshot.MatchMarkdownAsync();
115115
}
116116

117117
[Fact]
@@ -263,7 +263,7 @@ public async Task Compose_Loose_Subgraph_Files()
263263
snapshot.Add(subgraph, $"{subgraph.Name} Subgraph Configuration");
264264
}
265265

266-
snapshot.MatchSnapshot();
266+
await snapshot.MatchMarkdownAsync();
267267
}
268268

269269
[Fact]
@@ -326,9 +326,9 @@ await app.InvokeAsync(
326326
snapshot.Add(subgraph, $"{subgraph.Name} Subgraph Configuration");
327327
}
328328

329-
snapshot.MatchSnapshot();
329+
await snapshot.MatchMarkdownAsync();
330330
}
331-
331+
332332
[Fact]
333333
public async Task Compose_With_Tag()
334334
{
@@ -344,10 +344,10 @@ public async Task Compose_With_Tag()
344344

345345
var packageFile = CreateTempFile(Extensions.FusionPackage);
346346
var gatewayConfig = Path.Combine(
347-
Path.GetDirectoryName(packageFile)!,
347+
Path.GetDirectoryName(packageFile)!,
348348
Path.GetFileNameWithoutExtension(packageFile) + "-settings.json");
349349
File.Delete(packageFile);
350-
350+
351351
await File.WriteAllTextAsync(gatewayConfig, FileResource.Open("test2.gateway-config.json"), Encoding.UTF8);
352352

353353
// act
@@ -373,6 +373,6 @@ public async Task Compose_With_Tag()
373373
snapshot.Add(subgraph, $"{subgraph.Name} Subgraph Configuration");
374374
}
375375

376-
snapshot.MatchSnapshot();
376+
await snapshot.MatchMarkdownAsync();
377377
}
378378
}
Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
Schema Document
2-
---------------
1+
# Compose_Fusion_Graph
2+
3+
## Schema Document
4+
5+
```graphql
36
schema {
47
query: Query
58
mutation: Mutation
69
}
710

811
type Query {
12+
errorField: String
913
userById(id: ID!): User
1014
users: [User!]!
1115
usersById(ids: [ID!]!): [User!]!
@@ -26,6 +30,7 @@ type SomeData {
2630

2731
type User implements Node {
2832
birthdate: Date!
33+
errorField: String
2934
id: ID!
3035
name: String!
3136
username: String!
@@ -49,16 +54,18 @@ input AddUserInput {
4954

5055
"The `Date` scalar represents an ISO-8601 compliant date type."
5156
scalar Date
52-
---------------
57+
```
58+
59+
## Fusion Graph Document
5360

54-
Fusion Graph Document
55-
---------------
61+
```graphql
5662
schema @fusion(version: 1) @transport(subgraph: "Accounts", group: "Fusion", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") @transport(subgraph: "Accounts", group: "Fusion", location: "ws:\/\/localhost:5000\/graphql", kind: "WebSocket") {
5763
query: Query
5864
mutation: Mutation
5965
}
6066

6167
type Query {
68+
errorField: String @resolver(subgraph: "Accounts", select: "{ errorField }")
6269
userById(id: ID!): User @variable(subgraph: "Accounts", name: "id", argument: "id") @resolver(subgraph: "Accounts", select: "{ userById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ])
6370
users: [User!]! @resolver(subgraph: "Accounts", select: "{ users }")
6471
usersById(ids: [ID!]!): [User!]! @variable(subgraph: "Accounts", name: "ids", argument: "ids") @resolver(subgraph: "Accounts", select: "{ usersById(ids: $ids) }", arguments: [ { name: "ids", type: "[ID!]!" } ])
@@ -79,6 +86,7 @@ type SomeData {
7986

8087
type User implements Node @variable(subgraph: "Accounts", name: "User_id", select: "id") @resolver(subgraph: "Accounts", select: "{ userById(id: $User_id) }", arguments: [ { name: "User_id", type: "ID!" } ]) @resolver(subgraph: "Accounts", select: "{ usersById(ids: $User_id) }", arguments: [ { name: "User_id", type: "[ID!]!" } ], kind: "BATCH") {
8188
birthdate: Date! @source(subgraph: "Accounts")
89+
errorField: String @source(subgraph: "Accounts")
8290
id: ID! @source(subgraph: "Accounts")
8391
name: String! @source(subgraph: "Accounts")
8492
username: String! @source(subgraph: "Accounts")
@@ -102,13 +110,14 @@ input AddUserInput {
102110

103111
"The `Date` scalar represents an ISO-8601 compliant date type."
104112
scalar Date
105-
---------------
113+
```
106114

107-
Accounts Subgraph Configuration
108-
---------------
115+
## Accounts Subgraph Configuration
116+
117+
```json
109118
{
110119
"Name": "Accounts",
111-
"Schema": "schema {\n query: Query\n mutation: Mutation\n}\n\n\"The node interface is implemented by entities that have a global unique identifier.\"\ninterface Node {\n id: ID!\n}\n\ntype Query {\n \"Fetches an object given its ID.\"\n node(\"ID of the object.\" id: ID!): Node\n \"Lookup nodes by a list of IDs.\"\n nodes(\"The list of node IDs.\" ids: [ID!]!): [Node]!\n users: [User!]!\n userById(id: ID!): User\n usersById(ids: [ID!]!): [User!]!\n viewer: Viewer!\n}\n\ntype Mutation {\n addUser(input: AddUserInput!): AddUserPayload!\n}\n\n\"The `Date` scalar represents an ISO-8601 compliant date type.\"\nscalar Date\n\ntype Viewer {\n user: User\n data: SomeData!\n}\n\ntype User implements Node {\n id: ID!\n name: String!\n birthdate: Date!\n username: String!\n}\n\ntype SomeData {\n accountValue: String!\n}\n\ninput AddUserInput {\n name: String!\n username: String!\n birthdate: Date!\n}\n\ntype AddUserPayload {\n user: User\n}",
120+
"Schema": "schema {\n query: Query\n mutation: Mutation\n}\n\n\"The node interface is implemented by entities that have a global unique identifier.\"\ninterface Node {\n id: ID!\n}\n\ntype Query {\n \"Fetches an object given its ID.\"\n node(\"ID of the object.\" id: ID!): Node\n \"Lookup nodes by a list of IDs.\"\n nodes(\"The list of node IDs.\" ids: [ID!]!): [Node]!\n users: [User!]!\n userById(id: ID!): User\n usersById(ids: [ID!]!): [User!]!\n errorField: String\n viewer: Viewer!\n}\n\ntype Mutation {\n addUser(input: AddUserInput!): AddUserPayload!\n}\n\n\"The `Date` scalar represents an ISO-8601 compliant date type.\"\nscalar Date\n\ntype Viewer {\n user: User\n data: SomeData!\n}\n\ntype User implements Node {\n errorField: String\n id: ID!\n name: String!\n birthdate: Date!\n username: String!\n}\n\ntype SomeData {\n accountValue: String!\n}\n\ninput AddUserInput {\n name: String!\n username: String!\n birthdate: Date!\n}\n\ntype AddUserPayload {\n user: User\n}",
112121
"Extensions": [
113122
"extend type Query {\n userById(id: ID!\n @is(field: \"id\")): User!\n usersById(ids: [ID!]!\n @is(field: \"id\")): [User!]!\n}"
114123
],
@@ -124,4 +133,5 @@ Accounts Subgraph Configuration
124133
],
125134
"ConfigurationExtensions": null
126135
}
127-
---------------
136+
```
137+

0 commit comments

Comments
 (0)