From e895378168e437d63e3387435d49971a878b33dd Mon Sep 17 00:00:00 2001 From: Anton Gogolev Date: Thu, 4 May 2023 19:12:48 +0200 Subject: [PATCH] Fixed StrawberryShake result has empty Extensions Issue (#5967) --- .../src/Core/Json/JsonExtensionParser.cs | 18 ++++ .../Client/src/Core/OperationResultBuilder.cs | 11 ++- .../Client/src/Core/ResultFields.cs | 1 + .../Core.Tests/OperationResultBuilderTests.cs | 85 +++++++++++++++++++ .../GeneratorTestHelper.cs | 3 +- 5 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 src/StrawberryShake/Client/src/Core/Json/JsonExtensionParser.cs create mode 100644 src/StrawberryShake/Client/test/Core.Tests/OperationResultBuilderTests.cs diff --git a/src/StrawberryShake/Client/src/Core/Json/JsonExtensionParser.cs b/src/StrawberryShake/Client/src/Core/Json/JsonExtensionParser.cs new file mode 100644 index 00000000000..8201f68ac45 --- /dev/null +++ b/src/StrawberryShake/Client/src/Core/Json/JsonExtensionParser.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using System.Text.Json; + +namespace StrawberryShake.Json; + +internal static class JsonExtensionParser +{ + public static IReadOnlyDictionary? ParseExtensions(JsonElement result) + { + if (result is { ValueKind: JsonValueKind.Object }) + { + var extensions = JsonSerializationHelper.ReadValue(result); + return (IReadOnlyDictionary?)extensions; + } + + return null; + } +} diff --git a/src/StrawberryShake/Client/src/Core/OperationResultBuilder.cs b/src/StrawberryShake/Client/src/Core/OperationResultBuilder.cs index efc3e179a1e..bfef29c8944 100644 --- a/src/StrawberryShake/Client/src/Core/OperationResultBuilder.cs +++ b/src/StrawberryShake/Client/src/Core/OperationResultBuilder.cs @@ -25,6 +25,7 @@ public abstract class OperationResultBuilder TResultData? data = null; IOperationResultDataInfo? dataInfo = null; IReadOnlyList? errors = null; + IReadOnlyDictionary? extensions = null; try { @@ -42,6 +43,14 @@ public abstract class OperationResultBuilder { errors = JsonErrorParser.ParseErrors(errorsProp); } + + if (body.RootElement.TryGetProperty( + ResultFields.Extensions, + out var extensionsProp) && + extensionsProp.ValueKind is JsonValueKind.Object) + { + extensions = JsonExtensionParser.ParseExtensions(extensionsProp); + } } } catch (Exception ex) @@ -88,7 +97,7 @@ public abstract class OperationResultBuilder dataInfo, ResultDataFactory, errors, - response.Extensions, + extensions, response.ContextData); } diff --git a/src/StrawberryShake/Client/src/Core/ResultFields.cs b/src/StrawberryShake/Client/src/Core/ResultFields.cs index dad183da44d..ea35098e4a8 100644 --- a/src/StrawberryShake/Client/src/Core/ResultFields.cs +++ b/src/StrawberryShake/Client/src/Core/ResultFields.cs @@ -7,4 +7,5 @@ public static class ResultFields public const string Path = "path"; public const string Label = "label"; public const string HasNext = "hasNext"; + public const string Extensions = "extensions"; } diff --git a/src/StrawberryShake/Client/test/Core.Tests/OperationResultBuilderTests.cs b/src/StrawberryShake/Client/test/Core.Tests/OperationResultBuilderTests.cs new file mode 100644 index 00000000000..943c9496ddb --- /dev/null +++ b/src/StrawberryShake/Client/test/Core.Tests/OperationResultBuilderTests.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace StrawberryShake; + +public class OperationResultBuilderTests +{ + [Fact] + public void Build_With_Extensions() + { + // arrange + var factory = new DocumentDataFactory(); + var builder = new DocumentOperationResultBuilder(factory); + + // According to the current design, all implementations of IConnection operate + // on JsonDocuments which are deserialized straight from response streams and + // very few properties in the Response object are in fact ever initialized, + // including Response.Extensions. It is therefore safe to assume that, at + // least for now, OperationResultBuilder is the best place to actually parse + // and extract "extensions". + var body = JsonDocument.Parse(@"{""data"": { }, ""extensions"": { ""a"": 1, ""b"": { ""c"": ""Strawberry"" }, ""d"": 3.14 } }"); + var response = new Response(body, null); + + // act + var result = builder.Build(response); + + // assert + Assert.NotEmpty(result.Extensions); + Assert.Equal(1L, result.Extensions["a"]); + + var b = (IReadOnlyDictionary?)result.Extensions["b"]; + + Assert.NotNull(b); + Assert.Equal("Strawberry", b["c"]); + + Assert.Equal(3.14, result.Extensions["d"]); + } + + internal class Document + { + } + + internal class DocumentDataInfo : IOperationResultDataInfo + { + public IReadOnlyCollection EntityIds { get; } = ArraySegment.Empty; + + public ulong Version { get; } = 0; + + public IOperationResultDataInfo WithVersion(ulong version) + { + throw new NotImplementedException(); + } + } + + internal class DocumentDataFactory : IOperationResultDataFactory + { + public Type ResultType { get => typeof(Document); } + + public Document Create(IOperationResultDataInfo dataInfo, IEntityStoreSnapshot? snapshot = null) + { + return new Document(); + } + + object IOperationResultDataFactory.Create(IOperationResultDataInfo dataInfo, IEntityStoreSnapshot? snapshot) + { + return Create(dataInfo, snapshot); + } + } + + internal class DocumentOperationResultBuilder : OperationResultBuilder + { + public DocumentOperationResultBuilder(IOperationResultDataFactory resultDataFactory) + { + ResultDataFactory = resultDataFactory; + } + + protected override IOperationResultDataFactory ResultDataFactory { get; } + + protected override IOperationResultDataInfo BuildData(JsonElement obj) + { + return new DocumentDataInfo(); + } + } +} diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/GeneratorTestHelper.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/GeneratorTestHelper.cs index f954097e5e3..3a5063aa415 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/GeneratorTestHelper.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/GeneratorTestHelper.cs @@ -148,8 +148,7 @@ public static IReadOnlyList AssertError(params string[] fileNames) documents.ToString().MatchSnapshot(); } - IReadOnlyList diagnostics = - CSharpCompiler.GetDiagnosticErrors(documents.ToString()); + var diagnostics = CSharpCompiler.GetDiagnosticErrors(documents.ToString()); if (skipWarnings) {